Meta Description" name="description" />
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Obby: Easy, Normal, Divine</title>
<style>
body { margin: 0; overflow: hidden; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: #000; }
#ui { position: absolute; top: 20px; left: 20px; color: white; text-shadow: 2px 2px 4px #000; pointer-events: none; z-index: 10; }
#level-tag { font-size: 28px; font-weight: 900; letter-spacing: 2px; }
#controls-hint { font-size: 14px; opacity: 0.8; margin-top: 5px; }
#mobile-controls { position: absolute; bottom: 30px; width: 100%; display: none; justify-content: space-between; padding: 0 40px; box-sizing: border-box; }
.btn { width: 70px; height: 70px; background: rgba(255,255,255,0.2); border: 2px solid white; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; user-select: none; }
</style>
</head>
<body>
<div id="ui">
<div id="level-tag">LEVEL: EASY</div>
<div id="controls-hint">WASD to Move | SPACE to Jump</div>
</div>
<div id="mobile-controls">
<div class="btn" id="touch-move">MOVE</div>
<div class="btn" id="touch-jump">JUMP</div>
</div>
<script type="importmap">
{ "imports": { "three": "https://unpkg.com/three@0.160.0/build/three.module.js" } }
</script>
<script type="module">
import * as THREE from 'three';
let scene, camera, renderer, player;
let platforms = [];
let currentLevel = 1;
let moveForward = false, moveBackward = false, moveLeft = false, moveRight = false, canJump = false;
let velocity = new THREE.Vector3();
const clock = new THREE.Clock();
init();
animate();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const light = new THREE.HemisphereLight(0xffffff, 0x444444, 2);
scene.add(light);
const playerGeo = new THREE.CapsuleGeometry(0.5, 1, 4, 8);
const playerMat = new THREE.MeshStandardMaterial({ color: 0x00ffcc });
player = new THREE.Mesh(playerGeo, playerMat);
scene.add(player);
buildLevel(currentLevel);
window.addEventListener('keydown', (e) => handleKey(e.code, true));
window.addEventListener('keyup', (e) => handleKey(e.code, false));
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
if ('ontouchstart' in window) {
document.getElementById('mobile-controls').style.display = 'flex';
document.getElementById('touch-jump').addEventListener('touchstart', () => { if(canJump) velocity.y += 12; });
}
}
function buildLevel(lvl) {
platforms.forEach(p => scene.remove(p));
platforms = [];
const ui = document.getElementById('level-tag');
if (lvl === 1) {
ui.innerText = "LEVEL: EASY";
ui.style.color = "#00ff00";
scene.background = new THREE.Color(0x87ceeb);
addPlat(0, 0, 0, 10, 10, 0x2ecc71);
addPlat(0, 0, -15, 8, 8, 0x27ae60);
addPlat(0, 0, -30, 8, 8, 0x27ae60);
addWin(0, 0, -45);
} else if (lvl === 2) {
ui.innerText = "LEVEL: NORMAL";
ui.style.color = "#f1c40f";
scene.background = new THREE.Color(0xd35400);
addPlat(0, 0, 0, 5, 5, 0x7f8c8d);
addPlat(5, 2, -10, 4, 4, 0x95a5a6);
addPlat(-5, 4, -22, 4, 4, 0x95a5a6);
addPlat(0, 6, -35, 4, 4, 0x95a5a6);
addWin(0, 7, -50);
} else {
ui.innerText = "LEVEL: DIVINE";
ui.style.color = "#ffffff";
scene.background = new THREE.Color(0x1a1a2e);
addPlat(0, 0, 0, 4, 4, 0xffd700);
addPlat(0, 5, -15, 1.5, 1.5, 0xffffff);
addPlat(10, 10, -30, 1.2, 1.2, 0xffffff);
addPlat(-10, 15, -45, 1, 1, 0xffffff);
addWin(0, 22, -65);
}
player.position.set(0, 5, 0);
velocity.set(0,0,0);
}
function addPlat(x, y, z, w, d, col) {
const mesh = new THREE.Mesh(new THREE.BoxGeometry(w, 1, d), new THREE.MeshStandardMaterial({ color: col }));
mesh.position.set(x, y, z);
scene.add(mesh);
platforms.push(mesh);
}
function addWin(x, y, z) {
const mesh = new THREE.Mesh(new THREE.BoxGeometry(4, 0.5, 4), new THREE.MeshStandardMaterial({ color: 0xff00ff, emissive: 0xff00ff }));
mesh.position.set(x, y, z);
mesh.isWin = true;
scene.add(mesh);
platforms.push(mesh);
}
function handleKey(c, p) {
if (c === 'KeyW') moveForward = p;
if (c === 'KeyS') moveBackward = p;
if (c === 'KeyA') moveLeft = p;
if (c === 'KeyD') moveRight = p;
if (c === 'Space' && p && canJump) velocity.y += 12;
}
function animate() {
requestAnimationFrame(animate);
const d = clock.getDelta();
velocity.y -= 28 * d;
const s = 12;
if (moveForward) player.position.z -= s * d;
if (moveBackward) player.position.z += s * d;
if (moveLeft) player.position.x -= s * d;
if (moveRight) player.position.x += s * d;
player.position.y += velocity.y * d;
canJump = false;
platforms.forEach(p => {
const pB = new THREE.Box3().setFromObject(p);
const plB = new THREE.Box3().setFromObject(player);
if (plB.intersectsBox(pB) && velocity.y < 0 && player.position.y > p.position.y) {
player.position.y = p.position.y + 1.2;
velocity.y = 0;
canJump = true;
if (p.isWin) { currentLevel = currentLevel < 3 ? currentLevel + 1 : 1; buildLevel(currentLevel); }
}
});
if (player.position.y < -15) buildLevel(currentLevel);
camera.position.set(player.position.x, player.position.y + 6, player.position.z + 12);
camera.lookAt(player.position);
renderer.render(scene, camera);
}
</script>
</body>
</html>
2
2
258KB
1250KB
449.0ms
188.0ms
449.0ms