Here's my attempt at an Arkanoid/Breakout clone using JavaScript with ThreeJS. Let me haveThe feedback I'm looking for is more on the code side; the game itself is a work in progress. You can take it! for a spin by running the code snippet below.
(function() {
"use strict";
letconst paddleStates = {
MOVING_LEFT : 0,
MOVING_RIGHT : 1,
STATIONARY : 2
};
function createMeshAtPosition(meshProperties, position) {
let mesh = new THREE.Mesh(meshProperties.geometry, meshProperties.material);
mesh.position.copy(position);
return mesh;
}
function createFullScreenRenderer(elementId, settings) {
let renderer = new THREE.WebGLRenderer({
canvas: document.getElementById(elementId)
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(settings.backgroundColor);
return renderer;
}
function createCamera() {
let camera = new THREE.PerspectiveCamera(
90,
window.innerWidth / window.innerHeight,
0.1,
3000);
camera.position.set(0.0, 10.0, 0.0);
camera.lookAt(0.0, 0.0, -10.0);
return camera;
}
function makeResizeCallback(camera, renderer) {
return function() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
};
}
function makeKeyDownCallback(paddle, speed) {
return function(event) {
if (paddle.state ===== paddleStates.STATIONARY) {
if (event.key ===== "ArrowLeft") {
paddle.velocity.x = -speed;
paddle.state = paddleStates.MOVING_LEFT;
} else if (event.key ===== "ArrowRight") {
paddle.velocity.x = speed;
paddle.state = paddleStates.MOVING_RIGHT;
}
}
};
}
function makeKeyUpCallback(paddle) {
return function(event) {
if (paddle.state ===== paddleStates.MOVING_LEFT && event.key ===== "ArrowLeft") {
paddle.velocity.x = 0.0;
paddle.state = paddleStates.STATIONARY;
} else if (paddle.state ===== paddleStates.MOVING_RIGHT && event.key ===== "ArrowRight") {
paddle.velocity.x = 0.0;
paddle.state = paddleStates.STATIONARY;
}
};
}
function updatePosition(gameObject) {
gameObject.mesh.position.add(gameObject.velocity);
}
function resolveBallBlockCollision(ball, blockMesh, blockProperties, callback) {
if ((ball.mesh.position.z + ball.radius > blockMesh.position.z - blockProperties.height / 2 &&
(ball.mesh.position.z < blockMesh.position.z)) &&
(ball.mesh.position.x > blockMesh.position.x - blockProperties.width / 2) &&
(ball.mesh.position.x < blockMesh.position.x + blockProperties.width / 2) &&
(ball.velocity.z > 0.0))
{
ball.velocity.z *= -1.0;
callback();
return true;
}
if ((ball.mesh.position.z - ball.radius < blockMesh.position.z + blockProperties.height / 2 &&
(ball.mesh.position.z > blockMesh.position.z)) &&
(ball.mesh.position.x > blockMesh.position.x - blockProperties.width / 2) &&
(ball.mesh.position.x < blockMesh.position.x + blockProperties.width / 2) &&
(ball.velocity.z < 0.0))
{
ball.velocity.z *= -1.0;
callback();
return true;
}
if ((ball.mesh.position.x + ball.radius > blockMesh.position.x - blockProperties.width / 2 &&
(ball.mesh.position.x < blockMesh.position.x)) &&
(ball.mesh.position.z > blockMesh.position.z - blockProperties.height / 2) &&
(ball.mesh.position.z < blockMesh.position.z + blockProperties.height / 2) &&
(ball.velocity.x > 0.0))
{
ball.velocity.x *= -1.0;
callback();
return true;
}
if ((ball.mesh.position.x - ball.radius < blockMesh.position.x + blockProperties.width / 2 &&
(ball.mesh.position.x > blockMesh.position.x)) &&
(ball.mesh.position.z > blockMesh.position.z - blockProperties.height / 2) &&
(ball.mesh.position.z < blockMesh.position.z + blockProperties.height / 2) &&
(ball.velocity.x < 0.0))
{
ball.velocity.x *= -1.0;
callback();
return true;
}
return false;
}
function main() {
// Hard-coded "settings"
let settings = {
backgroundColor : 0x008888,
paddleSpeed : 0.3,
ballSpeed: 0.2
};
let paddle = {
width : 4,
height : 1,
depth : 1,
color : 0xffffff,
velocity : new THREE.Vector3(0.0, 0.0, 0.0),
state : paddleStates.STATIONARY,
startPosition : new THREE.Vector3(0.0, 0.0, -4.0)
};
let ball = {
radius : 0.5,
color : 0xffff00,
velocity : new THREE.Vector3(settings.ballSpeed, 0.0, -settings.ballSpeed),
startPosition : new THREE.Vector3(0.0, 0.0, -9.0),
segments : {
width : 16,
height : 16
}
};
const levelBounds = {
top : -35.0,
right : 17.0,
left : -17.0,
bottom : 0.0
};
const bricks = {
rows : 11,
columns : 11,
distanceFromEdges : 1.0,
distanceFromTop : 13.0,
spacing : 0.2,
color : 0xff00ff,
depth : 1.0
};
const lights = [
new THREE.AmbientLight(0xffffff, 0.5),
new THREE.PointLight(0xffffff, 0.5)
];
// Game
let renderer = createFullScreenRenderer("game-window", settings);
let scene = new THREE.Scene();
let camera = createCamera();
scene.add(camera);
paddle.mesh = createMeshAtPosition({
geometry : new THREE.BoxGeometry(paddle.width, paddle.depth, paddle.height),
material : new THREE.MeshLambertMaterial({ color : paddle.color })
}, paddle.startPosition);
scene.add(paddle.mesh);
ball.mesh = createMeshAtPosition({
geometry : new THREE.SphereGeometry(ball.radius, ball.segments.width, ball.segments.height),
material : new THREE.MeshLambertMaterial({ color: ball.color })
}, ball.startPosition);
scene.add(ball.mesh);
lights.forEach(light => scene.add(light));
const levelWidth = levelBounds.right - levelBounds.left;
const brick = {
width : (levelWidth - 2 * bricks.distanceFromEdges + bricks.spacing * (1 - bricks.columns)) / bricks.columns,
height : (bricks.distanceFromTop - bricks.distanceFromEdges) / bricks.rows,
depth : bricks.depth
};
let visibleBricks = [];
for (let row = 0; row < bricks.rows; row += 1) {
for (let column = 0; column < bricks.columns; column += 1) {
let position = new THREE.Vector3(
levelBounds.left + bricks.distanceFromEdges + column * (brick.width + bricks.spacing) + 0.5 * brick.width,
0.0,
levelBounds.top + bricks.distanceFromEdges + row * (brick.height + bricks.spacing) + 0.5 * brick.height);
let mesh = createMeshAtPosition({
geometry : new THREE.BoxGeometry(brick.width, brick.depth, brick.height),
material : new THREE.MeshLambertMaterial({ color : bricks.color })
}, position);
let name = `${row},${column}`;
mesh.name = name;
scene.add(mesh);
visibleBricks.push({
position : position,
name : name
});
}
}
requestAnimationFrame(render);
function render() {
// update paddle position
// ball-level collision
if ((ball.mesh.position.z - ball.radius < levelBounds.top && ball.velocity.z < 0.0) ||
(ball.mesh.position.z + ball.radius > levelBounds.bottom && ball.velocity.z > 0.0))
{
ball.velocity.z *= -1.0;
}
if ((ball.mesh.position.x + ball.radius > levelBounds.right && ball.velocity.x > 0.0) ||
(ball.mesh.position.x - ball.radius < levelBounds.left && ball.velocity.x < 0.0))
{
ball.velocity.x *= -1.0;
}
resolveBallBlockCollision(ball, paddle.mesh, paddle, function() {});
// ball-brick collision
for (let i = 0; i < visibleBricks.length; i += 1) {
let visibleBrick = visibleBricks[i];
let isCollided = resolveBallBlockCollision(ball, visibleBrick, brick, function() {
let selectedObject = scene.getObjectByName(visibleBrick.name);
scene.remove(selectedObject);
visibleBricks.splice(i, 1);
});
if (isCollided) {
break;
}
}
updatePosition(paddle);
updatePosition(ball);
renderer.render(scene, camera);
requestAnimationFrame(render);
}
window.addEventListener("resize", makeResizeCallback(camera, renderer), false);
window.addEventListener("keydown", makeKeyDownCallback(paddle, settings.paddleSpeed), false);
window.addEventListener("keyup", makeKeyUpCallback(paddle), false);
}
window.addEventListener("load", main, false);
})();
<!DOCTYPE html>
<html>
<head>
<title>Arkanoid</title>
<style>
body {
padding: 0px;
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<canvas id="game-window"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/95/three.js"></script>
</body>
</html>
Here's my attempt at an Arkanoid/Breakout clone using JavaScript with ThreeJS. Let me have it!
(function() {
"use strict";
let paddleStates = {
MOVING_LEFT : 0,
MOVING_RIGHT : 1,
STATIONARY : 2
};
function createMeshAtPosition(meshProperties, position) {
let mesh = new THREE.Mesh(meshProperties.geometry, meshProperties.material);
mesh.position.copy(position);
return mesh;
}
function createFullScreenRenderer(elementId, settings) {
let renderer = new THREE.WebGLRenderer({
canvas: document.getElementById(elementId)
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(settings.backgroundColor);
return renderer;
}
function createCamera() {
let camera = new THREE.PerspectiveCamera(
90,
window.innerWidth / window.innerHeight,
0.1,
3000);
camera.position.set(0.0, 10.0, 0.0);
camera.lookAt(0.0, 0.0, -10.0);
return camera;
}
function makeResizeCallback(camera, renderer) {
return function() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
};
}
function makeKeyDownCallback(paddle, speed) {
return function(event) {
if (paddle.state == paddleStates.STATIONARY) {
if (event.key == "ArrowLeft") {
paddle.velocity.x = -speed;
paddle.state = paddleStates.MOVING_LEFT;
} else if (event.key == "ArrowRight") {
paddle.velocity.x = speed;
paddle.state = paddleStates.MOVING_RIGHT;
}
}
};
}
function makeKeyUpCallback(paddle) {
return function(event) {
if (paddle.state == paddleStates.MOVING_LEFT && event.key == "ArrowLeft") {
paddle.velocity.x = 0.0;
paddle.state = paddleStates.STATIONARY;
} else if (paddle.state == paddleStates.MOVING_RIGHT && event.key == "ArrowRight") {
paddle.velocity.x = 0.0;
paddle.state = paddleStates.STATIONARY;
}
};
}
function updatePosition(gameObject) {
gameObject.mesh.position.add(gameObject.velocity);
}
function resolveBallBlockCollision(ball, blockMesh, blockProperties, callback) {
if ((ball.mesh.position.z + ball.radius > blockMesh.position.z - blockProperties.height / 2 &&
(ball.mesh.position.z < blockMesh.position.z)) &&
(ball.mesh.position.x > blockMesh.position.x - blockProperties.width / 2) &&
(ball.mesh.position.x < blockMesh.position.x + blockProperties.width / 2) &&
(ball.velocity.z > 0.0))
{
ball.velocity.z *= -1.0;
callback();
return true;
}
if ((ball.mesh.position.z - ball.radius < blockMesh.position.z + blockProperties.height / 2 &&
(ball.mesh.position.z > blockMesh.position.z)) &&
(ball.mesh.position.x > blockMesh.position.x - blockProperties.width / 2) &&
(ball.mesh.position.x < blockMesh.position.x + blockProperties.width / 2) &&
(ball.velocity.z < 0.0))
{
ball.velocity.z *= -1.0;
callback();
return true;
}
if ((ball.mesh.position.x + ball.radius > blockMesh.position.x - blockProperties.width / 2 &&
(ball.mesh.position.x < blockMesh.position.x)) &&
(ball.mesh.position.z > blockMesh.position.z - blockProperties.height / 2) &&
(ball.mesh.position.z < blockMesh.position.z + blockProperties.height / 2) &&
(ball.velocity.x > 0.0))
{
ball.velocity.x *= -1.0;
callback();
return true;
}
if ((ball.mesh.position.x - ball.radius < blockMesh.position.x + blockProperties.width / 2 &&
(ball.mesh.position.x > blockMesh.position.x)) &&
(ball.mesh.position.z > blockMesh.position.z - blockProperties.height / 2) &&
(ball.mesh.position.z < blockMesh.position.z + blockProperties.height / 2) &&
(ball.velocity.x < 0.0))
{
ball.velocity.x *= -1.0;
callback();
return true;
}
return false;
}
function main() {
// Hard-coded "settings"
let settings = {
backgroundColor : 0x008888,
paddleSpeed : 0.3,
ballSpeed: 0.2
};
let paddle = {
width : 4,
height : 1,
depth : 1,
color : 0xffffff,
velocity : new THREE.Vector3(0.0, 0.0, 0.0),
state : paddleStates.STATIONARY,
startPosition : new THREE.Vector3(0.0, 0.0, -4.0)
};
let ball = {
radius : 0.5,
color : 0xffff00,
velocity : new THREE.Vector3(settings.ballSpeed, 0.0, -settings.ballSpeed),
startPosition : new THREE.Vector3(0.0, 0.0, -9.0),
segments : {
width : 16,
height : 16
}
};
const levelBounds = {
top : -35.0,
right : 17.0,
left : -17.0,
bottom : 0.0
};
const bricks = {
rows : 11,
columns : 11,
distanceFromEdges : 1.0,
distanceFromTop : 13.0,
spacing : 0.2,
color : 0xff00ff,
depth : 1.0
};
const lights = [
new THREE.AmbientLight(0xffffff, 0.5),
new THREE.PointLight(0xffffff, 0.5)
];
// Game
let renderer = createFullScreenRenderer("game-window", settings);
let scene = new THREE.Scene();
let camera = createCamera();
scene.add(camera);
paddle.mesh = createMeshAtPosition({
geometry : new THREE.BoxGeometry(paddle.width, paddle.depth, paddle.height),
material : new THREE.MeshLambertMaterial({ color : paddle.color })
}, paddle.startPosition);
scene.add(paddle.mesh);
ball.mesh = createMeshAtPosition({
geometry : new THREE.SphereGeometry(ball.radius, ball.segments.width, ball.segments.height),
material : new THREE.MeshLambertMaterial({ color: ball.color })
}, ball.startPosition);
scene.add(ball.mesh);
lights.forEach(light => scene.add(light));
const levelWidth = levelBounds.right - levelBounds.left;
const brick = {
width : (levelWidth - 2 * bricks.distanceFromEdges + bricks.spacing * (1 - bricks.columns)) / bricks.columns,
height : (bricks.distanceFromTop - bricks.distanceFromEdges) / bricks.rows,
depth : bricks.depth
};
let visibleBricks = [];
for (let row = 0; row < bricks.rows; row += 1) {
for (let column = 0; column < bricks.columns; column += 1) {
let position = new THREE.Vector3(
levelBounds.left + bricks.distanceFromEdges + column * (brick.width + bricks.spacing) + 0.5 * brick.width,
0.0,
levelBounds.top + bricks.distanceFromEdges + row * (brick.height + bricks.spacing) + 0.5 * brick.height);
let mesh = createMeshAtPosition({
geometry : new THREE.BoxGeometry(brick.width, brick.depth, brick.height),
material : new THREE.MeshLambertMaterial({ color : bricks.color })
}, position);
let name = `${row},${column}`;
mesh.name = name;
scene.add(mesh);
visibleBricks.push({
position : position,
name : name
});
}
}
requestAnimationFrame(render);
function render() {
// update paddle position
// ball-level collision
if ((ball.mesh.position.z - ball.radius < levelBounds.top && ball.velocity.z < 0.0) ||
(ball.mesh.position.z + ball.radius > levelBounds.bottom && ball.velocity.z > 0.0))
{
ball.velocity.z *= -1.0;
}
if ((ball.mesh.position.x + ball.radius > levelBounds.right && ball.velocity.x > 0.0) ||
(ball.mesh.position.x - ball.radius < levelBounds.left && ball.velocity.x < 0.0))
{
ball.velocity.x *= -1.0;
}
resolveBallBlockCollision(ball, paddle.mesh, paddle, function() {});
// ball-brick collision
for (let i = 0; i < visibleBricks.length; i += 1) {
let visibleBrick = visibleBricks[i];
let isCollided = resolveBallBlockCollision(ball, visibleBrick, brick, function() {
let selectedObject = scene.getObjectByName(visibleBrick.name);
scene.remove(selectedObject);
visibleBricks.splice(i, 1);
});
if (isCollided) {
break;
}
}
updatePosition(paddle);
updatePosition(ball);
renderer.render(scene, camera);
requestAnimationFrame(render);
}
window.addEventListener("resize", makeResizeCallback(camera, renderer), false);
window.addEventListener("keydown", makeKeyDownCallback(paddle, settings.paddleSpeed), false);
window.addEventListener("keyup", makeKeyUpCallback(paddle), false);
}
window.addEventListener("load", main, false);
})();
<!DOCTYPE html>
<html>
<head>
<title>Arkanoid</title>
<style>
body {
padding: 0px;
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<canvas id="game-window"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/95/three.js"></script>
</body>
</html>
Here's my attempt at an Arkanoid/Breakout clone using JavaScript with ThreeJS. The feedback I'm looking for is more on the code side; the game itself is a work in progress. You can take it for a spin by running the code snippet below.
(function() {
"use strict";
const paddleStates = {
MOVING_LEFT : 0,
MOVING_RIGHT : 1,
STATIONARY : 2
};
function createMeshAtPosition(meshProperties, position) {
let mesh = new THREE.Mesh(meshProperties.geometry, meshProperties.material);
mesh.position.copy(position);
return mesh;
}
function createFullScreenRenderer(elementId, settings) {
let renderer = new THREE.WebGLRenderer({
canvas: document.getElementById(elementId)
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(settings.backgroundColor);
return renderer;
}
function createCamera() {
let camera = new THREE.PerspectiveCamera(
90,
window.innerWidth / window.innerHeight,
0.1,
3000);
camera.position.set(0.0, 10.0, 0.0);
camera.lookAt(0.0, 0.0, -10.0);
return camera;
}
function makeResizeCallback(camera, renderer) {
return function() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
};
}
function makeKeyDownCallback(paddle, speed) {
return function(event) {
if (paddle.state === paddleStates.STATIONARY) {
if (event.key === "ArrowLeft") {
paddle.velocity.x = -speed;
paddle.state = paddleStates.MOVING_LEFT;
} else if (event.key === "ArrowRight") {
paddle.velocity.x = speed;
paddle.state = paddleStates.MOVING_RIGHT;
}
}
};
}
function makeKeyUpCallback(paddle) {
return function(event) {
if (paddle.state === paddleStates.MOVING_LEFT && event.key === "ArrowLeft") {
paddle.velocity.x = 0.0;
paddle.state = paddleStates.STATIONARY;
} else if (paddle.state === paddleStates.MOVING_RIGHT && event.key === "ArrowRight") {
paddle.velocity.x = 0.0;
paddle.state = paddleStates.STATIONARY;
}
};
}
function updatePosition(gameObject) {
gameObject.mesh.position.add(gameObject.velocity);
}
function resolveBallBlockCollision(ball, blockMesh, blockProperties, callback) {
if ((ball.mesh.position.z + ball.radius > blockMesh.position.z - blockProperties.height / 2 &&
(ball.mesh.position.z < blockMesh.position.z)) &&
(ball.mesh.position.x > blockMesh.position.x - blockProperties.width / 2) &&
(ball.mesh.position.x < blockMesh.position.x + blockProperties.width / 2) &&
(ball.velocity.z > 0.0))
{
ball.velocity.z *= -1.0;
callback();
return true;
}
if ((ball.mesh.position.z - ball.radius < blockMesh.position.z + blockProperties.height / 2 &&
(ball.mesh.position.z > blockMesh.position.z)) &&
(ball.mesh.position.x > blockMesh.position.x - blockProperties.width / 2) &&
(ball.mesh.position.x < blockMesh.position.x + blockProperties.width / 2) &&
(ball.velocity.z < 0.0))
{
ball.velocity.z *= -1.0;
callback();
return true;
}
if ((ball.mesh.position.x + ball.radius > blockMesh.position.x - blockProperties.width / 2 &&
(ball.mesh.position.x < blockMesh.position.x)) &&
(ball.mesh.position.z > blockMesh.position.z - blockProperties.height / 2) &&
(ball.mesh.position.z < blockMesh.position.z + blockProperties.height / 2) &&
(ball.velocity.x > 0.0))
{
ball.velocity.x *= -1.0;
callback();
return true;
}
if ((ball.mesh.position.x - ball.radius < blockMesh.position.x + blockProperties.width / 2 &&
(ball.mesh.position.x > blockMesh.position.x)) &&
(ball.mesh.position.z > blockMesh.position.z - blockProperties.height / 2) &&
(ball.mesh.position.z < blockMesh.position.z + blockProperties.height / 2) &&
(ball.velocity.x < 0.0))
{
ball.velocity.x *= -1.0;
callback();
return true;
}
return false;
}
function main() {
// Hard-coded "settings"
let settings = {
backgroundColor : 0x008888,
paddleSpeed : 0.3,
ballSpeed: 0.2
};
let paddle = {
width : 4,
height : 1,
depth : 1,
color : 0xffffff,
velocity : new THREE.Vector3(0.0, 0.0, 0.0),
state : paddleStates.STATIONARY,
startPosition : new THREE.Vector3(0.0, 0.0, -4.0)
};
let ball = {
radius : 0.5,
color : 0xffff00,
velocity : new THREE.Vector3(settings.ballSpeed, 0.0, -settings.ballSpeed),
startPosition : new THREE.Vector3(0.0, 0.0, -9.0),
segments : {
width : 16,
height : 16
}
};
const levelBounds = {
top : -35.0,
right : 17.0,
left : -17.0,
bottom : 0.0
};
const bricks = {
rows : 11,
columns : 11,
distanceFromEdges : 1.0,
distanceFromTop : 13.0,
spacing : 0.2,
color : 0xff00ff,
depth : 1.0
};
const lights = [
new THREE.AmbientLight(0xffffff, 0.5),
new THREE.PointLight(0xffffff, 0.5)
];
// Game
let renderer = createFullScreenRenderer("game-window", settings);
let scene = new THREE.Scene();
let camera = createCamera();
scene.add(camera);
paddle.mesh = createMeshAtPosition({
geometry : new THREE.BoxGeometry(paddle.width, paddle.depth, paddle.height),
material : new THREE.MeshLambertMaterial({ color : paddle.color })
}, paddle.startPosition);
scene.add(paddle.mesh);
ball.mesh = createMeshAtPosition({
geometry : new THREE.SphereGeometry(ball.radius, ball.segments.width, ball.segments.height),
material : new THREE.MeshLambertMaterial({ color: ball.color })
}, ball.startPosition);
scene.add(ball.mesh);
lights.forEach(light => scene.add(light));
const levelWidth = levelBounds.right - levelBounds.left;
const brick = {
width : (levelWidth - 2 * bricks.distanceFromEdges + bricks.spacing * (1 - bricks.columns)) / bricks.columns,
height : (bricks.distanceFromTop - bricks.distanceFromEdges) / bricks.rows,
depth : bricks.depth
};
let visibleBricks = [];
for (let row = 0; row < bricks.rows; row += 1) {
for (let column = 0; column < bricks.columns; column += 1) {
let position = new THREE.Vector3(
levelBounds.left + bricks.distanceFromEdges + column * (brick.width + bricks.spacing) + 0.5 * brick.width,
0.0,
levelBounds.top + bricks.distanceFromEdges + row * (brick.height + bricks.spacing) + 0.5 * brick.height);
let mesh = createMeshAtPosition({
geometry : new THREE.BoxGeometry(brick.width, brick.depth, brick.height),
material : new THREE.MeshLambertMaterial({ color : bricks.color })
}, position);
let name = `${row},${column}`;
mesh.name = name;
scene.add(mesh);
visibleBricks.push({
position : position,
name : name
});
}
}
requestAnimationFrame(render);
function render() {
// update paddle position
// ball-level collision
if ((ball.mesh.position.z - ball.radius < levelBounds.top && ball.velocity.z < 0.0) ||
(ball.mesh.position.z + ball.radius > levelBounds.bottom && ball.velocity.z > 0.0))
{
ball.velocity.z *= -1.0;
}
if ((ball.mesh.position.x + ball.radius > levelBounds.right && ball.velocity.x > 0.0) ||
(ball.mesh.position.x - ball.radius < levelBounds.left && ball.velocity.x < 0.0))
{
ball.velocity.x *= -1.0;
}
resolveBallBlockCollision(ball, paddle.mesh, paddle, function() {});
// ball-brick collision
for (let i = 0; i < visibleBricks.length; i += 1) {
let visibleBrick = visibleBricks[i];
let isCollided = resolveBallBlockCollision(ball, visibleBrick, brick, function() {
let selectedObject = scene.getObjectByName(visibleBrick.name);
scene.remove(selectedObject);
visibleBricks.splice(i, 1);
});
if (isCollided) {
break;
}
}
updatePosition(paddle);
updatePosition(ball);
renderer.render(scene, camera);
requestAnimationFrame(render);
}
window.addEventListener("resize", makeResizeCallback(camera, renderer), false);
window.addEventListener("keydown", makeKeyDownCallback(paddle, settings.paddleSpeed), false);
window.addEventListener("keyup", makeKeyUpCallback(paddle), false);
}
window.addEventListener("load", main, false);
})();
<!DOCTYPE html>
<html>
<head>
<title>Arkanoid</title>
<style>
body {
padding: 0px;
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<canvas id="game-window"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/95/three.js"></script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Arkanoid</title>
<style>
body {
padding: 0px;
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<canvas id="game-window"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/95/three.js"></script>
<script>
(function() {
"use strict";
let paddleStates = {
MOVING_LEFT : 0,
MOVING_RIGHT : 1,
STATIONARY : 2
};
function createMeshAtPosition(meshProperties, position) {
let mesh = new THREE.Mesh(meshProperties.geometry, meshProperties.material);
mesh.position.copy(position);
return mesh;
}
function createFullScreenRenderer(elementId, settings) {
let renderer = new THREE.WebGLRenderer({
canvas: document.getElementById(elementId)
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(settings.backgroundColor);
return renderer;
}
function createCamera() {
let camera = new THREE.PerspectiveCamera(
90,
window.innerWidth / window.innerHeight,
0.1,
3000);
camera.position.set(0.0, 10.0, 0.0);
camera.lookAt(0.0, 0.0, -10.0);
return camera;
}
function makeResizeCallback(camera, renderer) {
return function() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
};
}
function makeKeyDownCallback(paddle, speed) {
return function(event) {
if (paddle.state == paddleStates.STATIONARY) {
if (event.key == "ArrowLeft") {
paddle.velocity.x = -speed;
paddle.state = paddleStates.MOVING_LEFT;
} else if (event.key == "ArrowRight") {
paddle.velocity.x = speed;
paddle.state = paddleStates.MOVING_RIGHT;
}
}
};
}
function makeKeyUpCallback(paddle) {
return function(event) {
if (paddle.state == paddleStates.MOVING_LEFT && event.key == "ArrowLeft") {
paddle.velocity.x = 0.0;
paddle.state = paddleStates.STATIONARY;
} else if (paddle.state == paddleStates.MOVING_RIGHT && event.key == "ArrowRight") {
paddle.velocity.x = 0.0;
paddle.state = paddleStates.STATIONARY;
}
};
}
function updatePosition(gameObject) {
gameObject.mesh.position.add(gameObject.velocity);
}
function resolveBallBlockCollision(ball, blockMesh, blockProperties, callback) {
if ((ball.mesh.position.z + ball.radius > blockMesh.position.z - blockProperties.height / 2 &&
(ball.mesh.position.z < blockMesh.position.z)) &&
(ball.mesh.position.x > blockMesh.position.x - blockProperties.width / 2) &&
(ball.mesh.position.x < blockMesh.position.x + blockProperties.width / 2) &&
(ball.velocity.z > 0.0))
{
ball.velocity.z *= -1.0;
callback();
return true;
}
if ((ball.mesh.position.z - ball.radius < blockMesh.position.z + blockProperties.height / 2 &&
(ball.mesh.position.z > blockMesh.position.z)) &&
(ball.mesh.position.x > blockMesh.position.x - blockProperties.width / 2) &&
(ball.mesh.position.x < blockMesh.position.x + blockProperties.width / 2) &&
(ball.velocity.z < 0.0))
{
ball.velocity.z *= -1.0;
callback();
return true;
}
if ((ball.mesh.position.x + ball.radius > blockMesh.position.x - blockProperties.width / 2 &&
(ball.mesh.position.x < blockMesh.position.x)) &&
(ball.mesh.position.z > blockMesh.position.z - blockProperties.height / 2) &&
(ball.mesh.position.z < blockMesh.position.z + blockProperties.height / 2) &&
(ball.velocity.x > 0.0))
{
ball.velocity.x *= -1.0;
callback();
return true;
}
if ((ball.mesh.position.x - ball.radius < blockMesh.position.x + blockProperties.width / 2 &&
(ball.mesh.position.x > blockMesh.position.x)) &&
(ball.mesh.position.z > blockMesh.position.z - blockProperties.height / 2) &&
(ball.mesh.position.z < blockMesh.position.z + blockProperties.height / 2) &&
(ball.velocity.x < 0.0))
{
ball.velocity.x *= -1.0;
callback();
return true;
}
return false;
}
function main() {
// Hard-coded "settings"
let settings = {
backgroundColor : 0x008888,
paddleSpeed : 0.3,
ballSpeed: 0.2
};
let paddle = {
width : 4,
height : 1,
depth : 1,
color : 0xffffff,
velocity : new THREE.Vector3(0.0, 0.0, 0.0),
state : paddleStates.STATIONARY,
startPosition : new THREE.Vector3(0.0, 0.0, -4.0)
};
let ball = {
radius : 0.5,
color : 0xffff00,
velocity : new THREE.Vector3(settings.ballSpeed, 0.0, -settings.ballSpeed),
startPosition : new THREE.Vector3(0.0, 0.0, -9.0),
segments : {
width : 16,
height : 16
}
};
const levelBounds = {
top : -35.0,
right : 17.0,
left : -17.0,
bottom : 0.0
};
const bricks = {
rows : 11,
columns : 11,
distanceFromEdges : 1.0,
distanceFromTop : 13.0,
spacing : 0.2,
color : 0xff00ff,
depth : 1.0
};
const lights = [
new THREE.AmbientLight(0xffffff, 0.5),
new THREE.PointLight(0xffffff, 0.5)
];
// Game
let renderer = createFullScreenRenderer("game-window", settings);
let scene = new THREE.Scene();
let camera = createCamera();
scene.add(camera);
paddle.mesh = createMeshAtPosition({
geometry : new THREE.BoxGeometry(paddle.width, paddle.depth, paddle.height),
material : new THREE.MeshLambertMaterial({ color : paddle.color })
}, paddle.startPosition);
scene.add(paddle.mesh);
ball.mesh = createMeshAtPosition({
geometry : new THREE.SphereGeometry(ball.radius, ball.segments.width, ball.segments.height),
material : new THREE.MeshLambertMaterial({ color: ball.color })
}, ball.startPosition);
scene.add(ball.mesh);
lights.forEach(light => scene.add(light));
const levelWidth = levelBounds.right - levelBounds.left;
const brick = {
width : (levelWidth - 2 * bricks.distanceFromEdges + bricks.spacing * (1 - bricks.columns)) / bricks.columns,
height : (bricks.distanceFromTop - bricks.distanceFromEdges) / bricks.rows,
depth : bricks.depth
};
let visibleBricks = [];
for (let row = 0; row < bricks.rows; row += 1) {
for (let column = 0; column < bricks.columns; column += 1) {
let position = new THREE.Vector3(
levelBounds.left + bricks.distanceFromEdges + column * (brick.width + bricks.spacing) + 0.5 * brick.width,
0.0,
levelBounds.top + bricks.distanceFromEdges + row * (brick.height + bricks.spacing) + 0.5 * brick.height);
let mesh = createMeshAtPosition({
geometry : new THREE.BoxGeometry(brick.width, brick.depth, brick.height),
material : new THREE.MeshLambertMaterial({ color : bricks.color })
}, position);
let name = `${row},${column}`;
mesh.name = name;
scene.add(mesh);
visibleBricks.push({
position : position,
name : name
});
}
}
requestAnimationFrame(render);
function render() {
// update paddle position
// ball-level collision
if ((ball.mesh.position.z - ball.radius < levelBounds.top && ball.velocity.z < 0.0) ||
(ball.mesh.position.z + ball.radius > levelBounds.bottom && ball.velocity.z > 0.0))
{
ball.velocity.z *= -1.0;
}
if ((ball.mesh.position.x + ball.radius > levelBounds.right && ball.velocity.x > 0.0) ||
(ball.mesh.position.x - ball.radius < levelBounds.left && ball.velocity.x < 0.0))
{
ball.velocity.x *= -1.0;
}
resolveBallBlockCollision(ball, paddle.mesh, paddle, function() {});
// ball-brick collision
for (let i = 0; i < visibleBricks.length; i += 1) {
let visibleBrick = visibleBricks[i];
let isCollided = resolveBallBlockCollision(ball, visibleBrick, brick, function() {
let selectedObject = scene.getObjectByName(visibleBrick.name);
scene.remove(selectedObject);
visibleBricks.splice(i, 1);
});
if (isCollided) {
break;
}
}
updatePosition(paddle);
updatePosition(ball);
renderer.render(scene, camera);
requestAnimationFrame(render);
}
window.addEventListener("resize", makeResizeCallback(camera, renderer), false);
window.addEventListener("keydown", makeKeyDownCallback(paddle, settings.paddleSpeed), false);
window.addEventListener("keyup", makeKeyUpCallback(paddle), false);
}
window.addEventListener("load", main, false);
})();
</script>
</body>
</html>
(function() {
"use strict";
let paddleStates = {
MOVING_LEFT : 0,
MOVING_RIGHT : 1,
STATIONARY : 2
};
function createMeshAtPosition(meshProperties, position) {
let mesh = new THREE.Mesh(meshProperties.geometry, meshProperties.material);
mesh.position.copy(position);
return mesh;
}
function createFullScreenRenderer(elementId, settings) {
let renderer = new THREE.WebGLRenderer({
canvas: document.getElementById(elementId)
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(settings.backgroundColor);
return renderer;
}
function createCamera() {
let camera = new THREE.PerspectiveCamera(
90,
window.innerWidth / window.innerHeight,
0.1,
3000);
camera.position.set(0.0, 10.0, 0.0);
camera.lookAt(0.0, 0.0, -10.0);
return camera;
}
function makeResizeCallback(camera, renderer) {
return function() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
};
}
function makeKeyDownCallback(paddle, speed) {
return function(event) {
if (paddle.state == paddleStates.STATIONARY) {
if (event.key == "ArrowLeft") {
paddle.velocity.x = -speed;
paddle.state = paddleStates.MOVING_LEFT;
} else if (event.key == "ArrowRight") {
paddle.velocity.x = speed;
paddle.state = paddleStates.MOVING_RIGHT;
}
}
};
}
function makeKeyUpCallback(paddle) {
return function(event) {
if (paddle.state == paddleStates.MOVING_LEFT && event.key == "ArrowLeft") {
paddle.velocity.x = 0.0;
paddle.state = paddleStates.STATIONARY;
} else if (paddle.state == paddleStates.MOVING_RIGHT && event.key == "ArrowRight") {
paddle.velocity.x = 0.0;
paddle.state = paddleStates.STATIONARY;
}
};
}
function updatePosition(gameObject) {
gameObject.mesh.position.add(gameObject.velocity);
}
function resolveBallBlockCollision(ball, blockMesh, blockProperties, callback) {
if ((ball.mesh.position.z + ball.radius > blockMesh.position.z - blockProperties.height / 2 &&
(ball.mesh.position.z < blockMesh.position.z)) &&
(ball.mesh.position.x > blockMesh.position.x - blockProperties.width / 2) &&
(ball.mesh.position.x < blockMesh.position.x + blockProperties.width / 2) &&
(ball.velocity.z > 0.0))
{
ball.velocity.z *= -1.0;
callback();
return true;
}
if ((ball.mesh.position.z - ball.radius < blockMesh.position.z + blockProperties.height / 2 &&
(ball.mesh.position.z > blockMesh.position.z)) &&
(ball.mesh.position.x > blockMesh.position.x - blockProperties.width / 2) &&
(ball.mesh.position.x < blockMesh.position.x + blockProperties.width / 2) &&
(ball.velocity.z < 0.0))
{
ball.velocity.z *= -1.0;
callback();
return true;
}
if ((ball.mesh.position.x + ball.radius > blockMesh.position.x - blockProperties.width / 2 &&
(ball.mesh.position.x < blockMesh.position.x)) &&
(ball.mesh.position.z > blockMesh.position.z - blockProperties.height / 2) &&
(ball.mesh.position.z < blockMesh.position.z + blockProperties.height / 2) &&
(ball.velocity.x > 0.0))
{
ball.velocity.x *= -1.0;
callback();
return true;
}
if ((ball.mesh.position.x - ball.radius < blockMesh.position.x + blockProperties.width / 2 &&
(ball.mesh.position.x > blockMesh.position.x)) &&
(ball.mesh.position.z > blockMesh.position.z - blockProperties.height / 2) &&
(ball.mesh.position.z < blockMesh.position.z + blockProperties.height / 2) &&
(ball.velocity.x < 0.0))
{
ball.velocity.x *= -1.0;
callback();
return true;
}
return false;
}
function main() {
// Hard-coded "settings"
let settings = {
backgroundColor : 0x008888,
paddleSpeed : 0.3,
ballSpeed: 0.2
};
let paddle = {
width : 4,
height : 1,
depth : 1,
color : 0xffffff,
velocity : new THREE.Vector3(0.0, 0.0, 0.0),
state : paddleStates.STATIONARY,
startPosition : new THREE.Vector3(0.0, 0.0, -4.0)
};
let ball = {
radius : 0.5,
color : 0xffff00,
velocity : new THREE.Vector3(settings.ballSpeed, 0.0, -settings.ballSpeed),
startPosition : new THREE.Vector3(0.0, 0.0, -9.0),
segments : {
width : 16,
height : 16
}
};
const levelBounds = {
top : -35.0,
right : 17.0,
left : -17.0,
bottom : 0.0
};
const bricks = {
rows : 11,
columns : 11,
distanceFromEdges : 1.0,
distanceFromTop : 13.0,
spacing : 0.2,
color : 0xff00ff,
depth : 1.0
};
const lights = [
new THREE.AmbientLight(0xffffff, 0.5),
new THREE.PointLight(0xffffff, 0.5)
];
// Game
let renderer = createFullScreenRenderer("game-window", settings);
let scene = new THREE.Scene();
let camera = createCamera();
scene.add(camera);
paddle.mesh = createMeshAtPosition({
geometry : new THREE.BoxGeometry(paddle.width, paddle.depth, paddle.height),
material : new THREE.MeshLambertMaterial({ color : paddle.color })
}, paddle.startPosition);
scene.add(paddle.mesh);
ball.mesh = createMeshAtPosition({
geometry : new THREE.SphereGeometry(ball.radius, ball.segments.width, ball.segments.height),
material : new THREE.MeshLambertMaterial({ color: ball.color })
}, ball.startPosition);
scene.add(ball.mesh);
lights.forEach(light => scene.add(light));
const levelWidth = levelBounds.right - levelBounds.left;
const brick = {
width : (levelWidth - 2 * bricks.distanceFromEdges + bricks.spacing * (1 - bricks.columns)) / bricks.columns,
height : (bricks.distanceFromTop - bricks.distanceFromEdges) / bricks.rows,
depth : bricks.depth
};
let visibleBricks = [];
for (let row = 0; row < bricks.rows; row += 1) {
for (let column = 0; column < bricks.columns; column += 1) {
let position = new THREE.Vector3(
levelBounds.left + bricks.distanceFromEdges + column * (brick.width + bricks.spacing) + 0.5 * brick.width,
0.0,
levelBounds.top + bricks.distanceFromEdges + row * (brick.height + bricks.spacing) + 0.5 * brick.height);
let mesh = createMeshAtPosition({
geometry : new THREE.BoxGeometry(brick.width, brick.depth, brick.height),
material : new THREE.MeshLambertMaterial({ color : bricks.color })
}, position);
let name = `${row},${column}`;
mesh.name = name;
scene.add(mesh);
visibleBricks.push({
position : position,
name : name
});
}
}
requestAnimationFrame(render);
function render() {
// update paddle position
// ball-level collision
if ((ball.mesh.position.z - ball.radius < levelBounds.top && ball.velocity.z < 0.0) ||
(ball.mesh.position.z + ball.radius > levelBounds.bottom && ball.velocity.z > 0.0))
{
ball.velocity.z *= -1.0;
}
if ((ball.mesh.position.x + ball.radius > levelBounds.right && ball.velocity.x > 0.0) ||
(ball.mesh.position.x - ball.radius < levelBounds.left && ball.velocity.x < 0.0))
{
ball.velocity.x *= -1.0;
}
resolveBallBlockCollision(ball, paddle.mesh, paddle, function() {});
// ball-brick collision
for (let i = 0; i < visibleBricks.length; i += 1) {
let visibleBrick = visibleBricks[i];
let isCollided = resolveBallBlockCollision(ball, visibleBrick, brick, function() {
let selectedObject = scene.getObjectByName(visibleBrick.name);
scene.remove(selectedObject);
visibleBricks.splice(i, 1);
});
if (isCollided) {
break;
}
}
updatePosition(paddle);
updatePosition(ball);
renderer.render(scene, camera);
requestAnimationFrame(render);
}
window.addEventListener("resize", makeResizeCallback(camera, renderer), false);
window.addEventListener("keydown", makeKeyDownCallback(paddle, settings.paddleSpeed), false);
window.addEventListener("keyup", makeKeyUpCallback(paddle), false);
}
window.addEventListener("load", main, false);
})();<!DOCTYPE html>
<html>
<head>
<title>Arkanoid</title>
<style>
body {
padding: 0px;
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<canvas id="game-window"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/95/three.js"></script>
</body>
</html><!DOCTYPE html>
<html>
<head>
<title>Arkanoid</title>
<style>
body {
padding: 0px;
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<canvas id="game-window"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/95/three.js"></script>
<script>
(function() {
"use strict";
let paddleStates = {
MOVING_LEFT : 0,
MOVING_RIGHT : 1,
STATIONARY : 2
};
function createMeshAtPosition(meshProperties, position) {
let mesh = new THREE.Mesh(meshProperties.geometry, meshProperties.material);
mesh.position.copy(position);
return mesh;
}
function createFullScreenRenderer(elementId, settings) {
let renderer = new THREE.WebGLRenderer({
canvas: document.getElementById(elementId)
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(settings.backgroundColor);
return renderer;
}
function createCamera() {
let camera = new THREE.PerspectiveCamera(
90,
window.innerWidth / window.innerHeight,
0.1,
3000);
camera.position.set(0.0, 10.0, 0.0);
camera.lookAt(0.0, 0.0, -10.0);
return camera;
}
function makeResizeCallback(camera, renderer) {
return function() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
};
}
function makeKeyDownCallback(paddle, speed) {
return function(event) {
if (paddle.state == paddleStates.STATIONARY) {
if (event.key == "ArrowLeft") {
paddle.velocity.x = -speed;
paddle.state = paddleStates.MOVING_LEFT;
} else if (event.key == "ArrowRight") {
paddle.velocity.x = speed;
paddle.state = paddleStates.MOVING_RIGHT;
}
}
};
}
function makeKeyUpCallback(paddle) {
return function(event) {
if (paddle.state == paddleStates.MOVING_LEFT && event.key == "ArrowLeft") {
paddle.velocity.x = 0.0;
paddle.state = paddleStates.STATIONARY;
} else if (paddle.state == paddleStates.MOVING_RIGHT && event.key == "ArrowRight") {
paddle.velocity.x = 0.0;
paddle.state = paddleStates.STATIONARY;
}
};
}
function updatePosition(gameObject) {
gameObject.mesh.position.add(gameObject.velocity);
}
function resolveBallBlockCollision(ball, blockMesh, blockProperties, callback) {
if ((ball.mesh.position.z + ball.radius > blockMesh.position.z - blockProperties.height / 2 &&
(ball.mesh.position.z < blockMesh.position.z)) &&
(ball.mesh.position.x > blockMesh.position.x - blockProperties.width / 2) &&
(ball.mesh.position.x < blockMesh.position.x + blockProperties.width / 2) &&
(ball.velocity.z > 0.0))
{
ball.velocity.z *= -1.0;
callback();
return true;
}
if ((ball.mesh.position.z - ball.radius < blockMesh.position.z + blockProperties.height / 2 &&
(ball.mesh.position.z > blockMesh.position.z)) &&
(ball.mesh.position.x > blockMesh.position.x - blockProperties.width / 2) &&
(ball.mesh.position.x < blockMesh.position.x + blockProperties.width / 2) &&
(ball.velocity.z < 0.0))
{
ball.velocity.z *= -1.0;
callback();
return true;
}
if ((ball.mesh.position.x + ball.radius > blockMesh.position.x - blockProperties.width / 2 &&
(ball.mesh.position.x < blockMesh.position.x)) &&
(ball.mesh.position.z > blockMesh.position.z - blockProperties.height / 2) &&
(ball.mesh.position.z < blockMesh.position.z + blockProperties.height / 2) &&
(ball.velocity.x > 0.0))
{
ball.velocity.x *= -1.0;
callback();
return true;
}
if ((ball.mesh.position.x - ball.radius < blockMesh.position.x + blockProperties.width / 2 &&
(ball.mesh.position.x > blockMesh.position.x)) &&
(ball.mesh.position.z > blockMesh.position.z - blockProperties.height / 2) &&
(ball.mesh.position.z < blockMesh.position.z + blockProperties.height / 2) &&
(ball.velocity.x < 0.0))
{
ball.velocity.x *= -1.0;
callback();
return true;
}
return false;
}
function main() {
// Hard-coded "settings"
let settings = {
backgroundColor : 0x008888,
paddleSpeed : 0.3,
ballSpeed: 0.2
};
let paddle = {
width : 4,
height : 1,
depth : 1,
color : 0xffffff,
velocity : new THREE.Vector3(0.0, 0.0, 0.0),
state : paddleStates.STATIONARY,
startPosition : new THREE.Vector3(0.0, 0.0, -4.0)
};
let ball = {
radius : 0.5,
color : 0xffff00,
velocity : new THREE.Vector3(settings.ballSpeed, 0.0, -settings.ballSpeed),
startPosition : new THREE.Vector3(0.0, 0.0, -9.0),
segments : {
width : 16,
height : 16
}
};
const levelBounds = {
top : -35.0,
right : 17.0,
left : -17.0,
bottom : 0.0
};
const bricks = {
rows : 11,
columns : 11,
distanceFromEdges : 1.0,
distanceFromTop : 13.0,
spacing : 0.2,
color : 0xff00ff,
depth : 1.0
};
const lights = [
new THREE.AmbientLight(0xffffff, 0.5),
new THREE.PointLight(0xffffff, 0.5)
];
// Game
let renderer = createFullScreenRenderer("game-window", settings);
let scene = new THREE.Scene();
let camera = createCamera();
scene.add(camera);
paddle.mesh = createMeshAtPosition({
geometry : new THREE.BoxGeometry(paddle.width, paddle.depth, paddle.height),
material : new THREE.MeshLambertMaterial({ color : paddle.color })
}, paddle.startPosition);
scene.add(paddle.mesh);
ball.mesh = createMeshAtPosition({
geometry : new THREE.SphereGeometry(ball.radius, ball.segments.width, ball.segments.height),
material : new THREE.MeshLambertMaterial({ color: ball.color })
}, ball.startPosition);
scene.add(ball.mesh);
lights.forEach(light => scene.add(light));
const levelWidth = levelBounds.right - levelBounds.left;
const brick = {
width : (levelWidth - 2 * bricks.distanceFromEdges + bricks.spacing * (1 - bricks.columns)) / bricks.columns,
height : (bricks.distanceFromTop - bricks.distanceFromEdges) / bricks.rows,
depth : bricks.depth
};
let visibleBricks = [];
for (let row = 0; row < bricks.rows; row += 1) {
for (let column = 0; column < bricks.columns; column += 1) {
let position = new THREE.Vector3(
levelBounds.left + bricks.distanceFromEdges + column * (brick.width + bricks.spacing) + 0.5 * brick.width,
0.0,
levelBounds.top + bricks.distanceFromEdges + row * (brick.height + bricks.spacing) + 0.5 * brick.height);
let mesh = createMeshAtPosition({
geometry : new THREE.BoxGeometry(brick.width, brick.depth, brick.height),
material : new THREE.MeshLambertMaterial({ color : bricks.color })
}, position);
let name = `${row},${column}`;
mesh.name = name;
scene.add(mesh);
visibleBricks.push({
position : position,
name : name
});
}
}
requestAnimationFrame(render);
function render() {
// update paddle position
// ball-level collision
if ((ball.mesh.position.z - ball.radius < levelBounds.top && ball.velocity.z < 0.0) ||
(ball.mesh.position.z + ball.radius > levelBounds.bottom && ball.velocity.z > 0.0))
{
ball.velocity.z *= -1.0;
}
if ((ball.mesh.position.x + ball.radius > levelBounds.right && ball.velocity.x > 0.0) ||
(ball.mesh.position.x - ball.radius < levelBounds.left && ball.velocity.x < 0.0))
{
ball.velocity.x *= -1.0;
}
resolveBallBlockCollision(ball, paddle.mesh, paddle, function() {});
// ball-brick collision
for (let i = 0; i < visibleBricks.length; i += 1) {
let visibleBrick = visibleBricks[i];
let isCollided = resolveBallBlockCollision(ball, visibleBrick, brick, function() {
let selectedObject = scene.getObjectByName(visibleBrick.name);
scene.remove(selectedObject);
visibleBricks.splice(i, 1);
});
if (isCollided) {
break;
}
}
updatePosition(paddle);
updatePosition(ball);
renderer.render(scene, camera);
requestAnimationFrame(render);
}
window.addEventListener("resize", makeResizeCallback(camera, renderer), false);
window.addEventListener("keydown", makeKeyDownCallback(paddle, settings.paddleSpeed), false);
window.addEventListener("keyup", makeKeyUpCallback(paddle), false);
}
window.addEventListener("load", main, false);
})();
</script>
</body>
</html>
(function() {
"use strict";
let paddleStates = {
MOVING_LEFT : 0,
MOVING_RIGHT : 1,
STATIONARY : 2
};
function createMeshAtPosition(meshProperties, position) {
let mesh = new THREE.Mesh(meshProperties.geometry, meshProperties.material);
mesh.position.copy(position);
return mesh;
}
function createFullScreenRenderer(elementId, settings) {
let renderer = new THREE.WebGLRenderer({
canvas: document.getElementById(elementId)
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(settings.backgroundColor);
return renderer;
}
function createCamera() {
let camera = new THREE.PerspectiveCamera(
90,
window.innerWidth / window.innerHeight,
0.1,
3000);
camera.position.set(0.0, 10.0, 0.0);
camera.lookAt(0.0, 0.0, -10.0);
return camera;
}
function makeResizeCallback(camera, renderer) {
return function() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
};
}
function makeKeyDownCallback(paddle, speed) {
return function(event) {
if (paddle.state == paddleStates.STATIONARY) {
if (event.key == "ArrowLeft") {
paddle.velocity.x = -speed;
paddle.state = paddleStates.MOVING_LEFT;
} else if (event.key == "ArrowRight") {
paddle.velocity.x = speed;
paddle.state = paddleStates.MOVING_RIGHT;
}
}
};
}
function makeKeyUpCallback(paddle) {
return function(event) {
if (paddle.state == paddleStates.MOVING_LEFT && event.key == "ArrowLeft") {
paddle.velocity.x = 0.0;
paddle.state = paddleStates.STATIONARY;
} else if (paddle.state == paddleStates.MOVING_RIGHT && event.key == "ArrowRight") {
paddle.velocity.x = 0.0;
paddle.state = paddleStates.STATIONARY;
}
};
}
function updatePosition(gameObject) {
gameObject.mesh.position.add(gameObject.velocity);
}
function resolveBallBlockCollision(ball, blockMesh, blockProperties, callback) {
if ((ball.mesh.position.z + ball.radius > blockMesh.position.z - blockProperties.height / 2 &&
(ball.mesh.position.z < blockMesh.position.z)) &&
(ball.mesh.position.x > blockMesh.position.x - blockProperties.width / 2) &&
(ball.mesh.position.x < blockMesh.position.x + blockProperties.width / 2) &&
(ball.velocity.z > 0.0))
{
ball.velocity.z *= -1.0;
callback();
return true;
}
if ((ball.mesh.position.z - ball.radius < blockMesh.position.z + blockProperties.height / 2 &&
(ball.mesh.position.z > blockMesh.position.z)) &&
(ball.mesh.position.x > blockMesh.position.x - blockProperties.width / 2) &&
(ball.mesh.position.x < blockMesh.position.x + blockProperties.width / 2) &&
(ball.velocity.z < 0.0))
{
ball.velocity.z *= -1.0;
callback();
return true;
}
if ((ball.mesh.position.x + ball.radius > blockMesh.position.x - blockProperties.width / 2 &&
(ball.mesh.position.x < blockMesh.position.x)) &&
(ball.mesh.position.z > blockMesh.position.z - blockProperties.height / 2) &&
(ball.mesh.position.z < blockMesh.position.z + blockProperties.height / 2) &&
(ball.velocity.x > 0.0))
{
ball.velocity.x *= -1.0;
callback();
return true;
}
if ((ball.mesh.position.x - ball.radius < blockMesh.position.x + blockProperties.width / 2 &&
(ball.mesh.position.x > blockMesh.position.x)) &&
(ball.mesh.position.z > blockMesh.position.z - blockProperties.height / 2) &&
(ball.mesh.position.z < blockMesh.position.z + blockProperties.height / 2) &&
(ball.velocity.x < 0.0))
{
ball.velocity.x *= -1.0;
callback();
return true;
}
return false;
}
function main() {
// Hard-coded "settings"
let settings = {
backgroundColor : 0x008888,
paddleSpeed : 0.3,
ballSpeed: 0.2
};
let paddle = {
width : 4,
height : 1,
depth : 1,
color : 0xffffff,
velocity : new THREE.Vector3(0.0, 0.0, 0.0),
state : paddleStates.STATIONARY,
startPosition : new THREE.Vector3(0.0, 0.0, -4.0)
};
let ball = {
radius : 0.5,
color : 0xffff00,
velocity : new THREE.Vector3(settings.ballSpeed, 0.0, -settings.ballSpeed),
startPosition : new THREE.Vector3(0.0, 0.0, -9.0),
segments : {
width : 16,
height : 16
}
};
const levelBounds = {
top : -35.0,
right : 17.0,
left : -17.0,
bottom : 0.0
};
const bricks = {
rows : 11,
columns : 11,
distanceFromEdges : 1.0,
distanceFromTop : 13.0,
spacing : 0.2,
color : 0xff00ff,
depth : 1.0
};
const lights = [
new THREE.AmbientLight(0xffffff, 0.5),
new THREE.PointLight(0xffffff, 0.5)
];
// Game
let renderer = createFullScreenRenderer("game-window", settings);
let scene = new THREE.Scene();
let camera = createCamera();
scene.add(camera);
paddle.mesh = createMeshAtPosition({
geometry : new THREE.BoxGeometry(paddle.width, paddle.depth, paddle.height),
material : new THREE.MeshLambertMaterial({ color : paddle.color })
}, paddle.startPosition);
scene.add(paddle.mesh);
ball.mesh = createMeshAtPosition({
geometry : new THREE.SphereGeometry(ball.radius, ball.segments.width, ball.segments.height),
material : new THREE.MeshLambertMaterial({ color: ball.color })
}, ball.startPosition);
scene.add(ball.mesh);
lights.forEach(light => scene.add(light));
const levelWidth = levelBounds.right - levelBounds.left;
const brick = {
width : (levelWidth - 2 * bricks.distanceFromEdges + bricks.spacing * (1 - bricks.columns)) / bricks.columns,
height : (bricks.distanceFromTop - bricks.distanceFromEdges) / bricks.rows,
depth : bricks.depth
};
let visibleBricks = [];
for (let row = 0; row < bricks.rows; row += 1) {
for (let column = 0; column < bricks.columns; column += 1) {
let position = new THREE.Vector3(
levelBounds.left + bricks.distanceFromEdges + column * (brick.width + bricks.spacing) + 0.5 * brick.width,
0.0,
levelBounds.top + bricks.distanceFromEdges + row * (brick.height + bricks.spacing) + 0.5 * brick.height);
let mesh = createMeshAtPosition({
geometry : new THREE.BoxGeometry(brick.width, brick.depth, brick.height),
material : new THREE.MeshLambertMaterial({ color : bricks.color })
}, position);
let name = `${row},${column}`;
mesh.name = name;
scene.add(mesh);
visibleBricks.push({
position : position,
name : name
});
}
}
requestAnimationFrame(render);
function render() {
// update paddle position
// ball-level collision
if ((ball.mesh.position.z - ball.radius < levelBounds.top && ball.velocity.z < 0.0) ||
(ball.mesh.position.z + ball.radius > levelBounds.bottom && ball.velocity.z > 0.0))
{
ball.velocity.z *= -1.0;
}
if ((ball.mesh.position.x + ball.radius > levelBounds.right && ball.velocity.x > 0.0) ||
(ball.mesh.position.x - ball.radius < levelBounds.left && ball.velocity.x < 0.0))
{
ball.velocity.x *= -1.0;
}
resolveBallBlockCollision(ball, paddle.mesh, paddle, function() {});
// ball-brick collision
for (let i = 0; i < visibleBricks.length; i += 1) {
let visibleBrick = visibleBricks[i];
let isCollided = resolveBallBlockCollision(ball, visibleBrick, brick, function() {
let selectedObject = scene.getObjectByName(visibleBrick.name);
scene.remove(selectedObject);
visibleBricks.splice(i, 1);
});
if (isCollided) {
break;
}
}
updatePosition(paddle);
updatePosition(ball);
renderer.render(scene, camera);
requestAnimationFrame(render);
}
window.addEventListener("resize", makeResizeCallback(camera, renderer), false);
window.addEventListener("keydown", makeKeyDownCallback(paddle, settings.paddleSpeed), false);
window.addEventListener("keyup", makeKeyUpCallback(paddle), false);
}
window.addEventListener("load", main, false);
})();<!DOCTYPE html>
<html>
<head>
<title>Arkanoid</title>
<style>
body {
padding: 0px;
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<canvas id="game-window"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/95/three.js"></script>
</body>
</html>