Meta Description" name="description" />
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Voxel Sandbox Mobile</title>
<style>
body { margin:0; overflow:hidden; background:black; }
#ui {
position: fixed;
bottom: 10px;
width: 100%;
text-align: center;
color: white;
font-family: sans-serif;
}
#crosshair {
position: fixed;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
font-size: 20px;
color: white;
}
</style>
</head>
<body>
<div id="crosshair">+</div>
<div id="ui">Block: <span id="block">Grass</span></div>
<script src="https://cdn.jsdelivr.net/npm/three@0.158/build/three.min.js"></script>
<script>
// ===== SETUP =====
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x87CEEB);
const camera = new THREE.PerspectiveCamera(75, innerWidth/innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
// LIGHT
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(50,100,50);
scene.add(light);
// ===== BLOCK TYPES =====
const BLOCK = {
GRASS: 0,
DIRT: 1,
STONE: 2
};
const materials = [
new THREE.MeshLambertMaterial({color: 0x00aa00}),
new THREE.MeshLambertMaterial({color: 0x8B4513}),
new THREE.MeshLambertMaterial({color: 0x888888})
];
// ===== WORLD =====
const world = {};
const size = 32;
const height = 16;
function noise(x,z){
return Math.floor((Math.sin(x*0.2)+Math.cos(z*0.2))*2 + 6);
}
// generate terrain
for(let x=0;x<size;x++){
for(let z=0;z<size;z++){
let h = noise(x,z);
for(let y=0;y<h;y++){
let type = BLOCK.STONE;
if(y > h-2) type = BLOCK.GRASS;
else if(y > h-4) type = BLOCK.DIRT;
world[`${x},${y},${z}`] = type;
}
}
}
// ===== MESH =====
const meshes = {};
function createBlock(x,y,z,type){
const geo = new THREE.BoxGeometry(1,1,1);
const mesh = new THREE.Mesh(geo, materials[type]);
mesh.position.set(x,y,z);
scene.add(mesh);
meshes[`${x},${y},${z}`] = mesh;
}
// render visible blocks only
for(let key in world){
let [x,y,z] = key.split(',').map(Number);
let neighbors = [
`${x+1},${y},${z}`,
`${x-1},${y},${z}`,
`${x},${y+1},${z}`,
`${x},${y-1},${z}`,
`${x},${y},${z+1}`,
`${x},${y},${z-1}`
];
let visible = neighbors.some(n => !world[n]);
if(visible){
createBlock(x,y,z,world[key]);
}
}
// ===== PLAYER =====
camera.position.set(10,10,10);
let velocity = new THREE.Vector3();
let keys = {};
window.addEventListener('keydown', e => keys[e.key]=true);
window.addEventListener('keyup', e => keys[e.key]=false);
// TOUCH LOOK
let touchX=0, touchY=0;
window.addEventListener('touchmove', e=>{
let t = e.touches[0];
touchX = (t.clientX/window.innerWidth - 0.5)*2;
touchY = (t.clientY/window.innerHeight - 0.5)*2;
});
// ===== RAYCAST =====
const raycaster = new THREE.Raycaster();
function interact(place=false){
raycaster.setFromCamera({x:0,y:0}, camera);
const intersects = raycaster.intersectObjects(Object.values(meshes));
if(intersects.length > 0){
const obj = intersects[0].object;
const pos = obj.position;
if(place){
let nx = Math.round(pos.x + intersects[0].face.normal.x);
let ny = Math.round(pos.y + intersects[0].face.normal.y);
let nz = Math.round(pos.z + intersects[0].face.normal.z);
if(!world[`${nx},${ny},${nz}`]){
world[`${nx},${ny},${nz}`] = selectedBlock;
createBlock(nx,ny,nz,selectedBlock);
}
} else {
scene.remove(obj);
delete meshes[`${pos.x},${pos.y},${pos.z}`];
delete world[`${pos.x},${pos.y},${pos.z}`];
}
}
}
// tap = break, double tap = place
let lastTap = 0;
window.addEventListener('touchstart', ()=>{
let now = Date.now();
if(now - lastTap < 300){
interact(true);
} else {
interact(false);
}
lastTap = now;
});
// ===== HOTBAR =====
let selectedBlock = 0;
const names = ["Grass","Dirt","Stone"];
window.addEventListener('wheel', e=>{
selectedBlock = (selectedBlock + (e.deltaY>0?1:-1) + 3)%3;
document.getElementById("block").innerText = names[selectedBlock];
});
// ===== LOOP =====
function animate(){
requestAnimationFrame(animate);
// movement
let speed = 0.1;
if(keys['w']) camera.position.z -= speed;
if(keys['s']) camera.position.z += speed;
if(keys['a']) camera.position.x -= speed;
if(keys['d']) camera.position.x += speed;
// look
camera.rotation.y = -touchX;
camera.rotation.x = -touchY;
renderer.render(scene, camera);
}
animate();
</script>
</body>
</html>2
2
163KB
641KB
1,789.0ms
172.0ms
1,789.0ms