MultiBlox - Part 1
by DriftBear16342 lines10.1 KB🛠️ Three.js (3D graphics)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>MultiBlox - Part 1</title>
<style>
:root {
--bg-dark: #0b0e14;
--panel-bg: rgba(18, 22, 34, 0.85);
--accent-blue: #00d2ff;
--accent-purple: #7928ca;
--accent-gold: #ffb703;
--text-main: #f8fafc;
--text-muted: #94a3b8;
--border: rgba(255, 255, 255, 0.12);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
user-select: none;
-webkit-user-select: none;
}
body, html {
width: 100%;
height: 100%;
overflow: hidden;
background-color: var(--bg-dark);
font-family: 'Segoe UI', Roboto, sans-serif;
color: var(--text-main);
}
#canvas-container {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 1;
}
#ui-layer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 10;
pointer-events: none;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 16px;
}
.interactive {
pointer-events: auto;
}
.top-bar {
display: flex;
justify-content: space-between;
align-items: center;
background: var(--panel-bg);
backdrop-filter: blur(12px);
border: 1px solid var(--border);
border-radius: 12px;
padding: 10px 20px;
}
.brand-logo {
font-size: 20px;
font-weight: 900;
letter-spacing: 1px;
background: linear-gradient(135deg, var(--accent-blue), var(--accent-purple));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
text-transform: uppercase;
}
.stats-group {
display: flex;
align-items: center;
gap: 16px;
}
.stat-badge {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: 14px;
font-weight: 600;
background: rgba(255, 255, 255, 0.05);
padding: 6px 14px;
border-radius: 20px;
border: 1px solid var(--border);
}
.currency-icon {
color: var(--accent-gold);
}
.crosshair {
position: absolute;
top: 50%;
left: 50%;
width: 6px;
height: 6px;
background: rgba(255, 255, 255, 0.5);
border-radius: 50%;
transform: translate(-50%, -50%);
pointer-events: none;
}
.instructions {
background: var(--panel-bg);
border: 1px solid var(--border);
border-radius: 8px;
padding: 8px 14px;
font-size: 12px;
color: var(--text-muted);
width: fit-content;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
</head>
<body>
<div id="canvas-container"></div>
<div id="ui-layer">
<div class="top-bar interactive">
<div class="brand-logo">MultiBlox</div>
<div class="stats-group">
<div class="stat-badge">
<span class="currency-icon">⛃</span>
<span id="multibux-val">100</span>
<span style="font-size: 11px; color: var(--text-muted);">(₹<span id="inr-val">500</span>)</span>
</div>
<div class="stat-badge">
<span>Score: <span id="score-val">0</span></span>
</div>
</div>
</div>
<div class="crosshair"></div>
<div class="instructions">
Click screen to lock mouse • <b>WASD</b> to Move • <b>SPACE</b> to Jump • <b>SHIFT</b> to Sprint
</div>
</div>
<script>
// --- 1. Scene, Camera & Renderer ---
const container = document.getElementById('canvas-container');
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0b0e14);
scene.fog = new THREE.FogExp2(0x0b0e14, 0.015);
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
container.appendChild(renderer.domElement);
// --- 2. Lighting ---
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x223344, 0.6);
scene.add(hemiLight);
const sunLight = new THREE.DirectionalLight(0xfffaed, 0.9);
sunLight.position.set(30, 50, 20);
sunLight.castShadow = true;
sunLight.shadow.mapSize.width = 2048;
sunLight.shadow.mapSize.height = 2048;
scene.add(sunLight);
// --- 3. Arena Platform & Grid ---
const arenaSize = 100;
const floorGeo = new THREE.PlaneGeometry(arenaSize, arenaSize);
const floorMat = new THREE.MeshStandardMaterial({ color: 0x161b26, roughness: 0.8 });
const floorMesh = new THREE.Mesh(floorGeo, floorMat);
floorMesh.rotation.x = -Math.PI / 2;
floorMesh.receiveShadow = true;
scene.add(floorMesh);
const grid = new THREE.GridHelper(arenaSize, 50, 0x00d2ff, 0x243049);
grid.position.y = 0.01;
scene.add(grid);
// --- 4. 3D Character Rig ---
const playerGroup = new THREE.Group();
// Torso
const torso = new THREE.Mesh(
new THREE.BoxGeometry(1.2, 1.4, 0.8),
new THREE.MeshStandardMaterial({ color: 0x00d2ff, roughness: 0.3 })
);
torso.position.y = 1.6;
torso.castShadow = true;
playerGroup.add(torso);
// Head
const head = new THREE.Mesh(
new THREE.BoxGeometry(0.8, 0.8, 0.8),
new THREE.MeshStandardMaterial({ color: 0xf8fafc, roughness: 0.4 })
);
head.position.y = 2.7;
head.castShadow = true;
playerGroup.add(head);
// Legs
const legGeo = new THREE.BoxGeometry(0.5, 1.0, 0.5);
const legMat = new THREE.MeshStandardMaterial({ color: 0x1e293b, roughness: 0.5 });
const leftLeg = new THREE.Mesh(legGeo, legMat);
leftLeg.position.set(-0.35, 0.5, 0);
leftLeg.castShadow = true;
playerGroup.add(leftLeg);
const rightLeg = new THREE.Mesh(legGeo, legMat);
rightLeg.position.set(0.35, 0.5, 0);
rightLeg.castShadow = true;
playerGroup.add(rightLeg);
scene.add(playerGroup);
// --- 5. Movement & Camera Controls ---
const Player = {
speed: 12,
sprintMultiplier: 1.6,
jumpForce: 13,
velocity: new THREE.Vector3(),
isGrounded: true
};
let cameraPitch = 0.3;
let cameraYaw = 0;
const cameraDistance = 8;
const keys = {};
window.addEventListener('keydown', (e) => { keys[e.code] = true; });
window.addEventListener('keyup', (e) => { keys[e.code] = false; });
let isLocked = false;
renderer.domElement.addEventListener('click', () => {
renderer.domElement.requestPointerLock();
});
document.addEventListener('pointerlockchange', () => {
isLocked = document.pointerLockElement === renderer.domElement;
});
window.addEventListener('mousemove', (e) => {
if (!isLocked) return;
cameraYaw -= e.movementX * 0.003;
cameraPitch -= e.movementY * 0.003;
cameraPitch = Math.max(-0.2, Math.min(1.2, cameraPitch));
});
// --- 6. Physics & Animation Loop ---
const clock = new THREE.Clock();
const gravity = 30;
function update(dt) {
const forward = new THREE.Vector3(-Math.sin(cameraYaw), 0, -Math.cos(cameraYaw));
const right = new THREE.Vector3(Math.cos(cameraYaw), 0, -Math.sin(cameraYaw));
let moveZ = 0;
let moveX = 0;
if (keys['KeyW'] || keys['ArrowUp']) moveZ += 1;
if (keys['KeyS'] || keys['ArrowDown']) moveZ -= 1;
if (keys['KeyA'] || keys['ArrowLeft']) moveX -= 1;
if (keys['KeyD'] || keys['ArrowRight']) moveX += 1;
const moveDir = new THREE.Vector3()
.addScaledVector(forward, moveZ)
.addScaledVector(right, moveX);
const isSprinting = keys['ShiftLeft'] || keys['ShiftRight'];
const speed = Player.speed * (isSprinting ? Player.sprintMultiplier : 1.0);
if (moveDir.lengthSq() > 0.001) {
moveDir.normalize();
playerGroup.position.x += moveDir.x * speed * dt;
playerGroup.position.z += moveDir.z * speed * dt;
playerGroup.rotation.y = Math.atan2(moveDir.x, moveDir.z);
const walkCycle = Math.sin(clock.getElapsedTime() * (isSprinting ? 16 : 10));
leftLeg.rotation.x = walkCycle * 0.6;
rightLeg.rotation.x = -walkCycle * 0.6;
} else {
leftLeg.rotation.x = 0;
rightLeg.rotation.x = 0;
}
// Jump & Gravity
if (keys['Space'] && Player.isGrounded) {
Player.velocity.y = Player.jumpForce;
Player.isGrounded = false;
}
Player.velocity.y -= gravity * dt;
playerGroup.position.y += Player.velocity.y * dt;
if (playerGroup.position.y <= 0) {
playerGroup.position.y = 0;
Player.velocity.y = 0;
Player.isGrounded = true;
}
// Camera Follow Orbit
const targetPos = playerGroup.position.clone().add(new THREE.Vector3(0, 1.8, 0));
const camOffset = new THREE.Vector3(
Math.sin(cameraYaw) * Math.cos(cameraPitch) * cameraDistance,
Math.sin(cameraPitch) * cameraDistance,
Math.cos(cameraYaw) * Math.cos(cameraPitch) * cameraDistance
);
camera.position.copy(targetPos).add(camOffset);
camera.lookAt(targetPos);
}
function animate() {
requestAnimationFrame(animate);
const dt = Math.min(clock.getDelta(), 0.1);
update(dt);
renderer.render(scene, camera);
}
animate();
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
</script>
</body>
</html>Game Source: MultiBlox - Part 1
Creator: DriftBear16
Libraries: three
Complexity: complex (342 lines, 10.1 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: multiblox-part-1-driftbear16" to link back to the original. Then publish at arcadelab.ai/publish.