Meta Description" name="description" />
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Obby 3D - Langsung Main</title>
<style>
body { margin: 0; overflow: hidden; background: #87CEEB; font-family: Arial, sans-serif; }
#info {
position: absolute; top: 10px; left: 10px; color: white;
background: rgba(0,0,0,0.5); padding: 10px; border-radius: 8px;
font-size: 14px; z-index: 10;
}
#win {
position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
background: rgba(0,0,0,0.8); color: #00ff88; padding: 30px; border-radius: 15px;
text-align: center; font-size: 24px; display: none; z-index: 20;
}
#joystick {
position: absolute; bottom: 30px; left: 30px; width: 100px; height: 100px;
background: rgba(255,255,255,0.2); border-radius: 50%; display: none;
}
#jumpBtn {
position: absolute; bottom: 40px; right: 30px; width: 80px; height: 80px;
background: rgba(255,255,255,0.3); border: 3px solid white; border-radius: 50%;
color: white; font-size: 16px; font-weight: bold; display: none;
}
</style>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.160.0/build/three.module.js"
}
}
</script>
</head>
<body>
<div id="info">
**Obby 3D**<br>
PC: WASD + Spasi untuk lompat<br>
HP: Joystick kiri + tombol lompat<br>
Checkpoint: 0/3 | Jatuh: 0
</div>
<div id="win">π MENANG! π<br><small>Refresh untuk main lagi</small></div>
<div id="joystick"></div>
<button id="jumpBtn">LOMPAT</button>
<script type="module">
import * as THREE from 'three';
let scene, camera, renderer, player, clock;
let velocity = new THREE.Vector3();
let onGround = false;
let checkpoints = 0, falls = 0;
let keys = {};
let isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
// Setup
scene = new THREE.Scene();
scene.background = new THREE.Color(0x87CEEB);
scene.fog = new THREE.Fog(0x87CEEB, 0, 100);
camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
clock = new THREE.Clock();
// Lighting
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(10, 20, 10);
light.castShadow = true;
scene.add(light);
scene.add(new THREE.AmbientLight(0x666666));
// Player
const playerGeo = new THREE.BoxGeometry(0.8, 1.6, 0.8);
const playerMat = new THREE.MeshLambertMaterial({color: 0xff4444});
player = new THREE.Mesh(playerGeo, playerMat);
player.position.set(0, 2, 0);
player.castShadow = true;
scene.add(player);
// Platforms
function createPlatform(x, y, z, w, h, d, color, isCheckpoint = false, isKill = false) {
const geo = new THREE.BoxGeometry(w, h, d);
const mat = new THREE.MeshLambertMaterial({color: color});
const plat = new THREE.Mesh(geo, mat);
plat.position.set(x, y, z);
plat.receiveShadow = true;
plat.userData = {isCheckpoint, isKill, touched: false};
scene.add(plat);
return plat;
}
// Map Obby
createPlatform(0, 0, 0, 6, 1, 6, 0x00ff00); // Start
createPlatform(0, 0, -8, 3, 1, 3, 0x4444ff);
createPlatform(5, 1, -12, 3, 1, 3, 0x4444ff);
createPlatform(-3, 2, -16, 2, 1, 2, 0x4444ff);
createPlatform(0, 2, -20, 4, 1, 4, 0xffaa00, true); // Checkpoint 1
createPlatform(0, 3, -26, 1.5, 1, 6, 0x4444ff); // Tipis
createPlatform(-6, 4, -30, 3, 1, 3, 0x4444ff);
createPlatform(6, 5, -34, 2, 1, 2, 0xff4444, false, true); // Kill brick
createPlatform(0, 5, -38, 3, 1, 3, 0x4444ff);
createPlatform(0, 6, -44, 4, 1, 4, 0xffaa00, true); // Checkpoint 2
createPlatform(4, 7, -50, 1, 1, 1, 0x4444ff);
createPlatform(-4, 8, -54, 1, 1, 1, 0x4444ff);
createPlatform(0, 9, -60, 6, 1, 6, 0x00ff00, true); // Finish
// Controls PC
document.addEventListener('keydown', e => keys[e.key.toLowerCase()] = true);
document.addEventListener('keyup', e => keys[e.key.toLowerCase()] = false);
// Controls Mobile
if(isMobile) {
document.getElementById('joystick').style.display = 'block';
document.getElementById('jumpBtn').style.display = 'block';
let joyX = 0, joyY = 0;
const joystick = document.getElementById('joystick');
joystick.addEventListener('touchmove', e => {
const rect = joystick.getBoundingClientRect();
const touch = e.touches[0];
joyX = (touch.clientX - rect.left - 50) / 50;
joyY = (touch.clientY - rect.top - 50) / 50;
});
joystick.addEventListener('touchend', () => { joyX = 0; joyY = 0; });
document.getElementById('jumpBtn').addEventListener('touchstart', () => {
if(onGround) velocity.y = 8;
});
// Update movement from joystick
setInterval(() => {
keys['w'] = joyY < -0.3;
keys['s'] = joyY > 0.3;
keys['a'] = joyX < -0.3;
keys['d'] = joyX > 0.3;
}, 16);
}
// Update UI
function updateUI() {
document.getElementById('info').innerHTML = `
**Obby 3D**<br>
PC: WASD + Spasi untuk lompat<br>
HP: Joystick kiri + tombol lompat<br>
Checkpoint: ${checkpoints}/3 | Jatuh: ${falls}
`;
}
// Collision + Logic
function checkCollision() {
onGround = false;
scene.children.forEach(obj => {
if(obj.geometry && obj.geometry.type === 'BoxGeometry' && obj!== player) {
const playerBox = new THREE.Box3().setFromObject(player);
const platBox = new THREE.Box3().setFromObject(obj);
if(playerBox.intersectsBox(platBox)) {
// Di atas platform
if(player.position.y > obj.position.y && velocity.y <= 0) {
player.position.y = obj.position.y + obj.geometry.parameters.height/2 + 0.8;
velocity.y = 0;
onGround = true;
// Checkpoint
if(obj.userData.isCheckpoint &&!obj.userData.touched) {
obj.userData.touched = true;
checkpoints++;
obj.material.color.setHex(0x00ff00);
updateUI();
}
// Kill brick
if(obj.userData.isKill) {
player.position.set(0, 2, 0);
velocity.set(0,0,0);
falls++;
updateUI();
}
}
}
}
});
// Jatuh
if(player.position.y < -10) {
player.position.set(0, 2, 0);
velocity.set(0,0,0);
falls++;
updateUI();
}
// Win
if(player.position.z < -58 && player.position.y > 8) {
document.getElementById('win').style.display = 'block';
}
}
// Game Loop
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
// Movement
const speed = 8;
if(keys['w']) velocity.z -= speed * delta;
if(keys['s']) velocity.z += speed * delta;
if(keys['a']) velocity.x -= speed * delta;
if(keys['d']) velocity.x += speed * delta;
if(keys[' '] && onGround) velocity.y = 8;
// Gravity
velocity.y -= 20 * delta;
// Apply
player.position.add(velocity.clone().multiplyScalar(delta));
velocity.x *= 0.9; // Friction
velocity.z *= 0.9;
checkCollision();
// Camera follow
camera.position.lerp(
new THREE.Vector3(player.position.x, player.position.y + 5, player.position.z + 10),
0.1
);
camera.lookAt(player.position);
renderer.render(scene, camera);
}
// Resize
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
updateUI();
animate();
</script>
</body>
</html>2
2
259KB
1251KB
337.0ms
108.0ms
338.0ms