In game controls.😅 Like mine craft.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MiniCraft: Creative Edition</title>
<style>
body { margin: 0; overflow: hidden; font-family: 'Segoe UI', Arial, sans-serif; background-color: #87CEEB; }
canvas { display: block; }
/* UI Overlay */
#ui {
position: absolute;
top: 20px;
left: 20px;
color: white;
text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
pointer-events: none;
}
#stats-bar {
position: absolute;
top: 20px;
right: 20px;
background: rgba(0,0,0,0.5);
padding: 10px 20px;
border-radius: 5px;
color: #afffba;
font-family: monospace;
font-size: 14px;
pointer-events: none;
}
#crosshair {
position: absolute;
top: 50%;
left: 50%;
width: 16px;
height: 16px;
border: 2px solid white;
transform: translate(-50%, -50%);
pointer-events: none;
}
#mining-progress-container {
position: absolute;
top: 55%;
left: 50%;
transform: translate(-50%, -50%);
width: 100px;
height: 6px;
background: rgba(0,0,0,0.5);
border: 1px solid white;
display: none;
}
#mining-progress-bar { width: 0%; height: 100%; background: #ffeb3b; }
/* Inventory Screen */
#inventory-screen {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 400px;
background: #c6c6c6;
border: 4px solid #555;
display: none;
padding: 20px;
grid-template-columns: repeat(5, 1fr);
gap: 10px;
box-shadow: 0 0 50px rgba(0,0,0,0.5);
z-index: 100;
}
.inv-slot {
width: 60px;
height: 60px;
background: #8b8b8b;
border: 2px solid #373737;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 24px;
}
.inv-slot:hover { background: #afafaf; border-color: white; }
/* HUD Hotbar */
#hotbar {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 5px;
background: rgba(0,0,0,0.4);
padding: 5px;
border-radius: 5px;
}
.hot-slot {
width: 40px;
height: 40px;
border: 2px solid #555;
background: #333;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 10px;
}
.hot-slot.active { border-color: white; background: #666; }
.instructions {
position: absolute;
bottom: 20px;
left: 20px;
background: rgba(0,0,0,0.6);
color: white;
padding: 10px;
border-radius: 5px;
font-size: 12px;
}
</style>
</head>
<body>
<div id="ui">
<h2 style="margin:0">MINICRAFT CREATIVE</h2>
<div id="mode-text">Double Space to Fly</div>
</div>
<div id="stats-bar">
C: <span id="val-c">0, 0, 0</span> |
S: <span id="val-s">777</span> |
H: <span id="val-h">100</span> |
SP: <span id="val-sp">0</span>
</div>
<div id="crosshair"></div>
<div id="mining-progress-container"><div id="mining-progress-bar"></div></div>
<div id="inventory-screen"></div>
<div class="instructions">
<b>WASD</b>: Move | <b>E</b>: Inventory | <b>Double Space</b>: Fly<br>
<b>Left Click</b>: Place | <b>Hold Right Click</b>: Mine
</div>
<div id="hotbar"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
const colors = {
grass: 0x4caf50, dirt: 0x8b4513, wood: 0xdeb887,
stone: 0x777777, chest: 0xcd853f, bed: 0xe91e63,
leaf: 0x2e7d32, glass: 0xaaddff, villager: 0xffdbac,
water: 0x2196f3
};
const itemData = [
{ id: 'shovel', icon: '🥄', type: 'tool' },
{ id: 'pickaxe', icon: '⛏️', type: 'tool' },
{ id: 'axe', icon: '🪓', type: 'tool' },
{ id: 'water', icon: '🪣', type: 'block' }, // Water bucket added
{ id: 'grass', icon: '🟩', type: 'block' },
{ id: 'dirt', icon: '🟫', type: 'block' },
{ id: 'wood', icon: '🪵', type: 'block' },
{ id: 'stone', icon: '🧱', type: 'block' },
{ id: 'chest', icon: '📦', type: 'block' },
{ id: 'bed', icon: '🛌', type: 'block' },
{ id: 'glass', icon: '💎', type: 'block' }
];
// --- SETUP ---
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x87CEEB);
scene.fog = new THREE.FogExp2(0x87CEEB, 0.03);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const light = new THREE.HemisphereLight(0xeeeeff, 0x777788, 1);
scene.add(light);
const objects = [];
const mobs = [];
// --- PLAYER STATE ---
let isFlying = false;
let inventoryOpen = false;
let activeItem = itemData[0];
let lastSpaceTime = 0;
const velocity = new THREE.Vector3();
const move = { forward: false, backward: false, left: false, right: false, up: false, down: false };
// --- UI INITIALIZATION ---
const invScreen = document.getElementById('inventory-screen');
const hotbar = document.getElementById('hotbar');
function updateUI() {
invScreen.innerHTML = '';
hotbar.innerHTML = '';
itemData.forEach((item, index) => {
// Inventory Slots
const slot = document.createElement('div');
slot.className = 'inv-slot';
slot.innerHTML = item.icon;
slot.onclick = () => { selectItem(item); toggleInventory(); };
invScreen.appendChild(slot);
// Hotbar Slots (first 9)
if (index < 9) {
const hSlot = document.createElement('div');
hSlot.className = 'hot-slot' + (activeItem.id === item.id ? ' active' : '');
hSlot.id = 'hot-' + index;
hSlot.innerHTML = item.icon;
hotbar.appendChild(hSlot);
}
});
}
function selectItem(item) {
activeItem = item;
updateUI();
}
function toggleInventory() {
inventoryOpen = !inventoryOpen;
invScreen.style.display = inventoryOpen ? 'grid' : 'none';
if (inventoryOpen) document.exitPointerLock();
else document.body.requestPointerLock();
}
updateUI();
// --- INPUT ---
document.addEventListener('keydown', (e) => {
if (e.code === 'KeyE') { toggleInventory(); return; }
if (inventoryOpen) return;
switch (e.code) {
case 'KeyW': move.forward = true; break;
case 'KeyS': move.backward = true; break;
case 'KeyA': move.left = true; break;
case 'KeyD': move.right = true; break;
case 'Digit1': selectItem(itemData[0]); break;
case 'Digit2': selectItem(itemData[1]); break;
case 'Digit3': selectItem(itemData[2]); break;
case 'Digit4': selectItem(itemData[3]); break;
case 'Digit5': selectItem(itemData[4]); break;
case 'Digit6': selectItem(itemData[5]); break;
case 'Digit7': selectItem(itemData[6]); break;
case 'Digit8': selectItem(itemData[7]); break;
case 'Digit9': selectItem(itemData[8]); break;
case 'Space':
const now = performance.now();
if (now - lastSpaceTime < 300) {
isFlying = !isFlying;
document.getElementById('mode-text').innerText = isFlying ? "Flying Mode" : "Walking Mode";
velocity.y = 0;
}
lastSpaceTime = now;
if (isFlying) move.up = true;
else if (camera.position.y <= 2.1) velocity.y = 0.2;
break;
case 'ShiftLeft': if (isFlying) move.down = true; break;
}
});
document.addEventListener('keyup', (e) => {
switch (e.code) {
case 'KeyW': move.forward = false; break;
case 'KeyS': move.backward = false; break;
case 'KeyA': move.left = false; break;
case 'KeyD': move.right = false; break;
case 'Space': move.up = false; break;
case 'ShiftLeft': move.down = false; break;
}
});
document.body.addEventListener('mousedown', () => { if(!inventoryOpen) document.body.requestPointerLock(); });
document.addEventListener('mousemove', (e) => {
if (document.pointerLockElement === document.body) {
camera.rotation.y -= e.movementX * 0.002;
camera.rotation.x -= e.movementY * 0.002;
camera.rotation.x = Math.max(-Math.PI/2, Math.min(Math.PI/2, camera.rotation.x));
}
});
// --- WORLD GEN ---
const blockGeo = new THREE.BoxGeometry(1, 1, 1);
function addBlock(x, y, z, type) {
const mat = new THREE.MeshLambertMaterial({ color: colors[type] || 0xffffff });
if (type === 'glass' || type === 'water') {
mat.transparent = true;
mat.opacity = type === 'water' ? 0.7 : 0.6;
}
const mesh = new THREE.Mesh(blockGeo, mat);
mesh.position.set(x, y, z);
mesh.name = type;
scene.add(mesh);
objects.push(mesh);
}
for(let x=-15; x<15; x++) {
for(let z=-15; z<15; z++) {
addBlock(x, 0, z, 'grass');
}
}
// Small Village House
for(let y=1; y<4; y++) {
for(let x=2; x<6; x++) {
for(let z=2; z<6; z++) {
if (x===2 || x===5 || z===2 || z===5) {
if (!(y<3 && x===3 && z===2)) addBlock(x, y, z, 'wood');
}
}
}
}
// --- PEOPLE (NPCs) ---
class Villager {
constructor(x, z) {
this.mesh = new THREE.Group();
const body = new THREE.Mesh(new THREE.BoxGeometry(0.6, 1.2, 0.4), new THREE.MeshLambertMaterial({color: 0x795548}));
const head = new THREE.Mesh(new THREE.BoxGeometry(0.4, 0.4, 0.4), new THREE.MeshLambertMaterial({color: 0xffdbac}));
head.position.y = 0.8;
this.mesh.add(body, head);
this.mesh.position.set(x, 0.6, z);
scene.add(this.mesh);
this.angle = Math.random() * Math.PI * 2;
mobs.push(this);
}
update(delta) {
this.mesh.position.x += Math.cos(this.angle) * delta * 1.5;
this.mesh.position.z += Math.sin(this.angle) * delta * 1.5;
if (Math.random() < 0.01) this.angle = Math.random() * Math.PI * 2;
if (Math.abs(this.mesh.position.x) > 14) this.angle += Math.PI;
const dist = this.mesh.position.distanceTo(camera.position);
if (dist < 5) {
this.mesh.lookAt(camera.position.x, 0.6, camera.position.z);
} else {
this.mesh.rotation.y = this.angle;
}
}
}
new Villager(5, 5);
new Villager(-3, 8);
// --- INTERACTION ---
const raycaster = new THREE.Raycaster();
let isMining = false;
let mineTarget = null;
let mineProgress = 0;
window.addEventListener('mousedown', (e) => {
if (inventoryOpen || document.pointerLockElement !== document.body) return;
if (e.button === 0 && activeItem.type === 'block') {
raycaster.setFromCamera({x:0, y:0}, camera);
const intersects = raycaster.intersectObjects(objects);
if (intersects.length > 0) {
const p = intersects[0].object.position.clone().add(intersects[0].face.normal);
addBlock(p.x, Math.round(p.y), p.z, activeItem.id);
}
} else if (e.button === 2) {
isMining = true;
}
});
window.addEventListener('mouseup', (e) => { if(e.button === 2) { isMining = false; mineProgress = 0; document.getElementById('mining-progress-container').style.display = 'none'; } });
// --- GAME LOOP ---
let prevTime = performance.now();
function animate() {
requestAnimationFrame(animate);
const time = performance.now();
const delta = (time - prevTime) / 1000;
if (document.pointerLockElement === document.body) {
const speed = isFlying ? 15 : 8;
const dir = new THREE.Vector3();
camera.getWorldDirection(dir);
dir.y = 0; dir.normalize();
const side = new THREE.Vector3().crossVectors(camera.up, dir).normalize();
if (move.forward) camera.position.addScaledVector(dir, speed * delta);
if (move.backward) camera.position.addScaledVector(dir, -speed * delta);
if (move.left) camera.position.addScaledVector(side, speed * delta);
if (move.right) camera.position.addScaledVector(side, -speed * delta);
if (isFlying) {
if (move.up) camera.position.y += speed * delta;
if (move.down) camera.position.y -= speed * delta;
} else {
velocity.y -= 9.8 * 0.05 * delta;
camera.position.y += velocity.y;
if (camera.position.y < 2) { camera.position.y = 2; velocity.y = 0; }
}
document.getElementById('val-c').innerText = `${camera.position.x.toFixed(1)}, ${camera.position.y.toFixed(1)}, ${camera.position.z.toFixed(1)}`;
document.getElementById('val-sp').innerText = (speed).toFixed(0);
if (isMining) {
raycaster.setFromCamera({x:0, y:0}, camera);
const intersects = raycaster.intersectObjects(objects);
if (intersects.length > 0 && intersects[0].object.position.y > 0) {
const target = intersects[0].object;
if (mineTarget !== target) { mineTarget = target; mineProgress = 0; }
mineProgress += delta * 4;
document.getElementById('mining-progress-container').style.display = 'block';
document.getElementById('mining-progress-bar').style.width = (mineProgress * 100) + '%';
if (mineProgress >= 1) {
scene.remove(target);
objects.splice(objects.indexOf(target), 1);
isMining = false;
document.getElementById('mining-progress-container').style.display = 'none';
}
}
}
}
mobs.forEach(m => m.update(delta));
renderer.render(scene, camera);
prevTime = time;
}
window.onload = () => { camera.position.set(0, 2, 5); animate(); };
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
window.addEventListener('contextmenu', e => e.preventDefault());
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MiniCraft: Creative Edition</title>
<style>
body { margin: 0; overflow: hidden; font-family: 'Segoe UI', Arial, sans-serif; background-color: #87CEEB; }
canvas { display: block; }
/* UI Overlay */
#ui {
position: absolute;
top: 20px;
left: 20px;
color: white;
text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
pointer-events: none;
}
#stats-bar {
position: absolute;
top: 20px;
right: 20px;
background: rgba(0,0,0,0.5);
padding: 10px 20px;
border-radius: 5px;
color: #afffba;
font-family: monospace;
font-size: 14px;
pointer-events: none;
}
#crosshair {
position: absolute;
top: 50%;
left: 50%;
width: 16px;
height: 16px;
border: 2px solid white;
transform: translate(-50%, -50%);
pointer-events: none;
}
#mining-progress-container {
position: absolute;
top: 55%;
left: 50%;
transform: translate(-50%, -50%);
width: 100px;
height: 6px;
background: rgba(0,0,0,0.5);
border: 1px solid white;
display: none;
}
#mining-progress-bar { width: 0%; height: 100%; background: #ffeb3b; }
/* Inventory Screen */
#inventory-screen {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 400px;
background: #c6c6c6;
border: 4px solid #555;
display: none;
padding: 20px;
grid-template-columns: repeat(5, 1fr);
gap: 10px;
box-shadow: 0 0 50px rgba(0,0,0,0.5);
z-index: 100;
}
.inv-slot {
width: 60px;
height: 60px;
background: #8b8b8b;
border: 2px solid #373737;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 24px;
}
.inv-slot:hover { background: #afafaf; border-color: white; }
/* HUD Hotbar */
#hotbar {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 5px;
background: rgba(0,0,0,0.4);
padding: 5px;
border-radius: 5px;
}
.hot-slot {
width: 40px;
height: 40px;
border: 2px solid #555;
background: #333;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 10px;
}
.hot-slot.active { border-color: white; background: #666; }
.instructions {
position: absolute;
bottom: 20px;
left: 20px;
background: rgba(0,0,0,0.6);
color: white;
padding: 10px;
border-radius: 5px;
font-size: 12px;
}
</style>
</head>
<body>
<div id="ui">
<h2 style="margin:0">MINICRAFT CREATIVE</h2>
<div id="mode-text">Double Space to Fly</div>
</div>
<div id="stats-bar">
C: <span id="val-c">0, 0, 0</span> |
S: <span id="val-s">777</span> |
H: <span id="val-h">100</span> |
SP: <span id="val-sp">0</span>
</div>
<div id="crosshair"></div>
<div id="mining-progress-container"><div id="mining-progress-bar"></div></div>
<div id="inventory-screen"></div>
<div class="instructions">
<b>WASD</b>: Move | <b>E</b>: Inventory | <b>Double Space</b>: Fly<br>
<b>Left Click</b>: Place | <b>Hold Right Click</b>: Mine
</div>
<div id="hotbar"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
const colors = {
grass: 0x4caf50, dirt: 0x8b4513, wood: 0xdeb887,
stone: 0x777777, chest: 0xcd853f, bed: 0xe91e63,
leaf: 0x2e7d32, glass: 0xaaddff, villager: 0xffdbac,
water: 0x2196f3
};
const itemData = [
{ id: 'shovel', icon: '🥄', type: 'tool' },
{ id: 'pickaxe', icon: '⛏️', type: 'tool' },
{ id: 'axe', icon: '🪓', type: 'tool' },
{ id: 'water', icon: '🪣', type: 'block' }, // Water bucket added
{ id: 'grass', icon: '🟩', type: 'block' },
{ id: 'dirt', icon: '🟫', type: 'block' },
{ id: 'wood', icon: '🪵', type: 'block' },
{ id: 'stone', icon: '🧱', type: 'block' },
{ id: 'chest', icon: '📦', type: 'block' },
{ id: 'bed', icon: '🛌', type: 'block' },
{ id: 'glass', icon: '💎', type: 'block' }
];
// --- SETUP ---
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x87CEEB);
scene.fog = new THREE.FogExp2(0x87CEEB, 0.03);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const light = new THREE.HemisphereLight(0xeeeeff, 0x777788, 1);
scene.add(light);
const objects = [];
const mobs = [];
// --- PLAYER STATE ---
let isFlying = false;
let inventoryOpen = false;
let activeItem = itemData[0];
let lastSpaceTime = 0;
const velocity = new THREE.Vector3();
const move = { forward: false, backward: false, left: false, right: false, up: false, down: false };
// --- UI INITIALIZATION ---
const invScreen = document.getElementById('inventory-screen');
const hotbar = document.getElementById('hotbar');
function updateUI() {
invScreen.innerHTML = '';
hotbar.innerHTML = '';
itemData.forEach((item, index) => {
// Inventory Slots
const slot = document.createElement('div');
slot.className = 'inv-slot';
slot.innerHTML = item.icon;
slot.onclick = () => { selectItem(item); toggleInventory(); };
invScreen.appendChild(slot);
// Hotbar Slots (first 9)
if (index < 9) {
const hSlot = document.createElement('div');
hSlot.className = 'hot-slot' + (activeItem.id === item.id ? ' active' : '');
hSlot.id = 'hot-' + index;
hSlot.innerHTML = item.icon;
hotbar.appendChild(hSlot);
}
});
}
function selectItem(item) {
activeItem = item;
updateUI();
}
function toggleInventory() {
inventoryOpen = !inventoryOpen;
invScreen.style.display = inventoryOpen ? 'grid' : 'none';
if (inventoryOpen) document.exitPointerLock();
else document.body.requestPointerLock();
}
updateUI();
// --- INPUT ---
document.addEventListener('keydown', (e) => {
if (e.code === 'KeyE') { toggleInventory(); return; }
if (inventoryOpen) return;
switch (e.code) {
case 'KeyW': move.forward = true; break;
case 'KeyS': move.backward = true; break;
case 'KeyA': move.left = true; break;
case 'KeyD': move.right = true; break;
case 'Digit1': selectItem(itemData[0]); break;
case 'Digit2': selectItem(itemData[1]); break;
case 'Digit3': selectItem(itemData[2]); break;
case 'Digit4': selectItem(itemData[3]); break;
case 'Digit5': selectItem(itemData[4]); break;
case 'Digit6': selectItem(itemData[5]); break;
case 'Digit7': selectItem(itemData[6]); break;
case 'Digit8': selectItem(itemData[7]); break;
case 'Digit9': selectItem(itemData[8]); break;
case 'Space':
const now = performance.now();
if (now - lastSpaceTime < 300) {
isFlying = !isFlying;
document.getElementById('mode-text').innerText = isFlying ? "Flying Mode" : "Walking Mode";
velocity.y = 0;
}
lastSpaceTime = now;
if (isFlying) move.up = true;
else if (camera.position.y <= 2.1) velocity.y = 0.2;
break;
case 'ShiftLeft': if (isFlying) move.down = true; break;
}
});
document.addEventListener('keyup', (e) => {
switch (e.code) {
case 'KeyW': move.forward = false; break;
case 'KeyS': move.backward = false; break;
case 'KeyA': move.left = false; break;
case 'KeyD': move.right = false; break;
case 'Space': move.up = false; break;
case 'ShiftLeft': move.down = false; break;
}
});
document.body.addEventListener('mousedown', () => { if(!inventoryOpen) document.body.requestPointerLock(); });
document.addEventListener('mousemove', (e) => {
if (document.pointerLockElement === document.body) {
camera.rotation.y -= e.movementX * 0.002;
camera.rotation.x -= e.movementY * 0.002;
camera.rotation.x = Math.max(-Math.PI/2, Math.min(Math.PI/2, camera.rotation.x));
}
});
// --- WORLD GEN ---
const blockGeo = new THREE.BoxGeometry(1, 1, 1);
function addBlock(x, y, z, type) {
const mat = new THREE.MeshLambertMaterial({ color: colors[type] || 0xffffff });
if (type === 'glass' || type === 'water') {
mat.transparent = true;
mat.opacity = type === 'water' ? 0.7 : 0.6;
}
const mesh = new THREE.Mesh(blockGeo, mat);
mesh.position.set(x, y, z);
mesh.name = type;
scene.add(mesh);
objects.push(mesh);
}
for(let x=-15; x<15; x++) {
for(let z=-15; z<15; z++) {
addBlock(x, 0, z, 'grass');
}
}
// Small Village House
for(let y=1; y<4; y++) {
for(let x=2; x<6; x++) {
for(let z=2; z<6; z++) {
if (x===2 || x===5 || z===2 || z===5) {
if (!(y<3 && x===3 && z===2)) addBlock(x, y, z, 'wood');
}
}
}
}
// --- PEOPLE (NPCs) ---
class Villager {
constructor(x, z) {
this.mesh = new THREE.Group();
const body = new THREE.Mesh(new THREE.BoxGeometry(0.6, 1.2, 0.4), new THREE.MeshLambertMaterial({color: 0x795548}));
const head = new THREE.Mesh(new THREE.BoxGeometry(0.4, 0.4, 0.4), new THREE.MeshLambertMaterial({color: 0xffdbac}));
head.position.y = 0.8;
this.mesh.add(body, head);
this.mesh.position.set(x, 0.6, z);
scene.add(this.mesh);
this.angle = Math.random() * Math.PI * 2;
mobs.push(this);
}
update(delta) {
this.mesh.position.x += Math.cos(this.angle) * delta * 1.5;
this.mesh.position.z += Math.sin(this.angle) * delta * 1.5;
if (Math.random() < 0.01) this.angle = Math.random() * Math.PI * 2;
if (Math.abs(this.mesh.position.x) > 14) this.angle += Math.PI;
const dist = this.mesh.position.distanceTo(camera.position);
if (dist < 5) {
this.mesh.lookAt(camera.position.x, 0.6, camera.position.z);
} else {
this.mesh.rotation.y = this.angle;
}
}
}
new Villager(5, 5);
new Villager(-3, 8);
// --- INTERACTION ---
const raycaster = new THREE.Raycaster();
let isMining = false;
let mineTarget = null;
let mineProgress = 0;
window.addEventListener('mousedown', (e) => {
if (inventoryOpen || document.pointerLockElement !== document.body) return;
if (e.button === 0 && activeItem.type === 'block') {
raycaster.setFromCamera({x:0, y:0}, camera);
const intersects = raycaster.intersectObjects(objects);
if (intersects.length > 0) {
const p = intersects[0].object.position.clone().add(intersects[0].face.normal);
addBlock(p.x, Math.round(p.y), p.z, activeItem.id);
}
} else if (e.button === 2) {
isMining = true;
}
});
window.addEventListener('mouseup', (e) => { if(e.button === 2) { isMining = false; mineProgress = 0; document.getElementById('mining-progress-container').style.display = 'none'; } });
// --- GAME LOOP ---
let prevTime = performance.now();
function animate() {
requestAnimationFrame(animate);
const time = performance.now();
const delta = (time - prevTime) / 1000;
if (document.pointerLockElement === document.body) {
const speed = isFlying ? 15 : 8;
const dir = new THREE.Vector3();
camera.getWorldDirection(dir);
dir.y = 0; dir.normalize();
const side = new THREE.Vector3().crossVectors(camera.up, dir).normalize();
if (move.forward) camera.position.addScaledVector(dir, speed * delta);
if (move.backward) camera.position.addScaledVector(dir, -speed * delta);
if (move.left) camera.position.addScaledVector(side, speed * delta);
if (move.right) camera.position.addScaledVector(side, -speed * delta);
if (isFlying) {
if (move.up) camera.position.y += speed * delta;
if (move.down) camera.position.y -= speed * delta;
} else {
velocity.y -= 9.8 * 0.05 * delta;
camera.position.y += velocity.y;
if (camera.position.y < 2) { camera.position.y = 2; velocity.y = 0; }
}
document.getElementById('val-c').innerText = `${camera.position.x.toFixed(1)}, ${camera.position.y.toFixed(1)}, ${camera.position.z.toFixed(1)}`;
document.getElementById('val-sp').innerText = (speed).toFixed(0);
if (isMining) {
raycaster.setFromCamera({x:0, y:0}, camera);
const intersects = raycaster.intersectObjects(objects);
if (intersects.length > 0 && intersects[0].object.position.y > 0) {
const target = intersects[0].object;
if (mineTarget !== target) { mineTarget = target; mineProgress = 0; }
mineProgress += delta * 4;
document.getElementById('mining-progress-container').style.display = 'block';
document.getElementById('mining-progress-bar').style.width = (mineProgress * 100) + '%';
if (mineProgress >= 1) {
scene.remove(target);
objects.splice(objects.indexOf(target), 1);
isMining = false;
document.getElementById('mining-progress-container').style.display = 'none';
}
}
}
}
mobs.forEach(m => m.update(delta));
renderer.render(scene, camera);
prevTime = time;
}
window.onload = () => { camera.position.set(0, 2, 5); animate(); };
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
window.addEventListener('contextmenu', e => e.preventDefault());
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MiniCraft: Creative Edition</title>
<style>
body { margin: 0; overflow: hidden; font-family: 'Segoe UI', Arial, sans-serif; background-color: #87CEEB; }
canvas { display: block; }
/* UI Overlay */
#ui {
position: absolute;
top: 20px;
left: 20px;
color: white;
text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
pointer-events: none;
}
#stats-bar {
position: absolute;
top: 20px;
right: 20px;
background: rgba(0,0,0,0.5);
padding: 10px 20px;
border-radius: 5px;
color: #afffba;
font-family: monospace;
font-size: 14px;
pointer-events: none;
}
#crosshair {
position: absolute;
top: 50%;
left: 50%;
width: 16px;
height: 16px;
border: 2px solid white;
transform: translate(-50%, -50%);
pointer-events: none;
}
#mining-progress-container {
position: absolute;
top: 55%;
left: 50%;
transform: translate(-50%, -50%);
width: 100px;
height: 6px;
background: rgba(0,0,0,0.5);
border: 1px solid white;
display: none;
}
#mining-progress-bar { width: 0%; height: 100%; background: #ffeb3b; }
/* Inventory Screen */
#inventory-screen {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 400px;
background: #c6c6c6;
border: 4px solid #555;
display: none;
padding: 20px;
grid-template-columns: repeat(5, 1fr);
gap: 10px;
box-shadow: 0 0 50px rgba(0,0,0,0.5);
z-index: 100;
}
.inv-slot {
width: 60px;
height: 60px;
background: #8b8b8b;
border: 2px solid #373737;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 24px;
}
.inv-slot:hover { background: #afafaf; border-color: white; }
/* HUD Hotbar */
#hotbar {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 5px;
background: rgba(0,0,0,0.4);
padding: 5px;
border-radius: 5px;
}
.hot-slot {
width: 40px;
height: 40px;
border: 2px solid #555;
background: #333;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 10px;
}
.hot-slot.active { border-color: white; background: #666; }
.instructions {
position: absolute;
bottom: 20px;
left: 20px;
background: rgba(0,0,0,0.6);
color: white;
padding: 10px;
border-radius: 5px;
font-size: 12px;
}
</style>
</head>
<body>
<div id="ui">
<h2 style="margin:0">MINICRAFT CREATIVE</h2>
<div id="mode-text">Double Space to Fly</div>
</div>
<div id="stats-bar">
C: <span id="val-c">0, 0, 0</span> |
S: <span id="val-s">777</span> |
H: <span id="val-h">100</span> |
SP: <span id="val-sp">0</span>
</div>
<div id="crosshair"></div>
<div id="mining-progress-container"><div id="mining-progress-bar"></div></div>
<div id="inventory-screen"></div>
<div class="instructions">
<b>WASD</b>: Move | <b>E</b>: Inventory | <b>Double Space</b>: Fly<br>
<b>Left Click</b>: Place | <b>Hold Right Click</b>: Mine
</div>
<div id="hotbar"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
const colors = {
grass: 0x4caf50, dirt: 0x8b4513, wood: 0xdeb887,
stone: 0x777777, chest: 0xcd853f, bed: 0xe91e63,
leaf: 0x2e7d32, glass: 0xaaddff, villager: 0xffdbac,
water: 0x2196f3
};
const itemData = [
{ id: 'shovel', icon: '🥄', type: 'tool' },
{ id: 'pickaxe', icon: '⛏️', type: 'tool' },
{ id: 'axe', icon: '🪓', type: 'tool' },
{ id: 'water', icon: '🪣', type: 'block' }, // Water bucket added
{ id: 'grass', icon: '🟩', type: 'block' },
{ id: 'dirt', icon: '🟫', type: 'block' },
{ id: 'wood', icon: '🪵', type: 'block' },
{ id: 'stone', icon: '🧱', type: 'block' },
{ id: 'chest', icon: '📦', type: 'block' },
{ id: 'bed', icon: '🛌', type: 'block' },
{ id: 'glass', icon: '💎', type: 'block' }
];
// --- SETUP ---
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x87CEEB);
scene.fog = new THREE.FogExp2(0x87CEEB, 0.03);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const light = new THREE.HemisphereLight(0xeeeeff, 0x777788, 1);
scene.add(light);
const objects = [];
const mobs = [];
// --- PLAYER STATE ---
let isFlying = false;
let inventoryOpen = false;
let activeItem = itemData[0];
let lastSpaceTime = 0;
const velocity = new THREE.Vector3();
const move = { forward: false, backward: false, left: false, right: false, up: false, down: false };
// --- UI INITIALIZATION ---
const invScreen = document.getElementById('inventory-screen');
const hotbar = document.getElementById('hotbar');
function updateUI() {
invScreen.innerHTML = '';
hotbar.innerHTML = '';
itemData.forEach((item, index) => {
// Inventory Slots
const slot = document.createElement('div');
slot.className = 'inv-slot';
slot.innerHTML = item.icon;
slot.onclick = () => { selectItem(item); toggleInventory(); };
invScreen.appendChild(slot);
// Hotbar Slots (first 9)
if (index < 9) {
const hSlot = document.createElement('div');
hSlot.className = 'hot-slot' + (activeItem.id === item.id ? ' active' : '');
hSlot.id = 'hot-' + index;
hSlot.innerHTML = item.icon;
hotbar.appendChild(hSlot);
}
});
}
function selectItem(item) {
activeItem = item;
updateUI();
}
function toggleInventory() {
inventoryOpen = !inventoryOpen;
invScreen.style.display = inventoryOpen ? 'grid' : 'none';
if (inventoryOpen) document.exitPointerLock();
else document.body.requestPointerLock();
}
updateUI();
// --- INPUT ---
document.addEventListener('keydown', (e) => {
if (e.code === 'KeyE') { toggleInventory(); return; }
if (inventoryOpen) return;
switch (e.code) {
case 'KeyW': move.forward = true; break;
case 'KeyS': move.backward = true; break;
case 'KeyA': move.left = true; break;
case 'KeyD': move.right = true; break;
case 'Digit1': selectItem(itemData[0]); break;
case 'Digit2': selectItem(itemData[1]); break;
case 'Digit3': selectItem(itemData[2]); break;
case 'Digit4': selectItem(itemData[3]); break;
case 'Digit5': selectItem(itemData[4]); break;
case 'Digit6': selectItem(itemData[5]); break;
case 'Digit7': selectItem(itemData[6]); break;
case 'Digit8': selectItem(itemData[7]); break;
case 'Digit9': selectItem(itemData[8]); break;
case 'Space':
const now = performance.now();
if (now - lastSpaceTime < 300) {
isFlying = !isFlying;
document.getElementById('mode-text').innerText = isFlying ? "Flying Mode" : "Walking Mode";
velocity.y = 0;
}
lastSpaceTime = now;
if (isFlying) move.up = true;
else if (camera.position.y <= 2.1) velocity.y = 0.2;
break;
case 'ShiftLeft': if (isFlying) move.down = true; break;
}
});
document.addEventListener('keyup', (e) => {
switch (e.code) {
case 'KeyW': move.forward = false; break;
case 'KeyS': move.backward = false; break;
case 'KeyA': move.left = false; break;
case 'KeyD': move.right = false; break;
case 'Space': move.up = false; break;
case 'ShiftLeft': move.down = false; break;
}
});
document.body.addEventListener('mousedown', () => { if(!inventoryOpen) document.body.requestPointerLock(); });
document.addEventListener('mousemove', (e) => {
if (document.pointerLockElement === document.body) {
camera.rotation.y -= e.movementX * 0.002;
camera.rotation.x -= e.movementY * 0.002;
camera.rotation.x = Math.max(-Math.PI/2, Math.min(Math.PI/2, camera.rotation.x));
}
});
// --- WORLD GEN ---
const blockGeo = new THREE.BoxGeometry(1, 1, 1);
function addBlock(x, y, z, type) {
const mat = new THREE.MeshLambertMaterial({ color: colors[type] || 0xffffff });
if (type === 'glass' || type === 'water') {
mat.transparent = true;
mat.opacity = type === 'water' ? 0.7 : 0.6;
}
const mesh = new THREE.Mesh(blockGeo, mat);
mesh.position.set(x, y, z);
mesh.name = type;
scene.add(mesh);
objects.push(mesh);
}
for(let x=-15; x<15; x++) {
for(let z=-15; z<15; z++) {
addBlock(x, 0, z, 'grass');
}
}
// Small Village House
for(let y=1; y<4; y++) {
for(let x=2; x<6; x++) {
for(let z=2; z<6; z++) {
if (x===2 || x===5 || z===2 || z===5) {
if (!(y<3 && x===3 && z===2)) addBlock(x, y, z, 'wood');
}
}
}
}
// --- PEOPLE (NPCs) ---
class Villager {
constructor(x, z) {
this.mesh = new THREE.Group();
const body = new THREE.Mesh(new THREE.BoxGeometry(0.6, 1.2, 0.4), new THREE.MeshLambertMaterial({color: 0x795548}));
const head = new THREE.Mesh(new THREE.BoxGeometry(0.4, 0.4, 0.4), new THREE.MeshLambertMaterial({color: 0xffdbac}));
head.position.y = 0.8;
this.mesh.add(body, head);
this.mesh.position.set(x, 0.6, z);
scene.add(this.mesh);
this.angle = Math.random() * Math.PI * 2;
mobs.push(this);
}
update(delta) {
this.mesh.position.x += Math.cos(this.angle) * delta * 1.5;
this.mesh.position.z += Math.sin(this.angle) * delta * 1.5;
if (Math.random() < 0.01) this.angle = Math.random() * Math.PI * 2;
if (Math.abs(this.mesh.position.x) > 14) this.angle += Math.PI;
const dist = this.mesh.position.distanceTo(camera.position);
if (dist < 5) {
this.mesh.lookAt(camera.position.x, 0.6, camera.position.z);
} else {
this.mesh.rotation.y = this.angle;
}
}
}
new Villager(5, 5);
new Villager(-3, 8);
// --- INTERACTION ---
const raycaster = new THREE.Raycaster();
let isMining = false;
let mineTarget = null;
let mineProgress = 0;
window.addEventListener('mousedown', (e) => {
if (inventoryOpen || document.pointerLockElement !== document.body) return;
if (e.button === 0 && activeItem.type === 'block') {
raycaster.setFromCamera({x:0, y:0}, camera);
const intersects = raycaster.intersectObjects(objects);
if (intersects.length > 0) {
const p = intersects[0].object.position.clone().add(intersects[0].face.normal);
addBlock(p.x, Math.round(p.y), p.z, activeItem.id);
}
} else if (e.button === 2) {
isMining = true;
}
});
window.addEventListener('mouseup', (e) => { if(e.button === 2) { isMining = false; mineProgress = 0; document.getElementById('mining-progress-container').style.display = 'none'; } });
// --- GAME LOOP ---
let prevTime = performance.now();
function animate() {
requestAnimationFrame(animate);
const time = performance.now();
const delta = (time - prevTime) / 1000;
if (document.pointerLockElement === document.body) {
const speed = isFlying ? 15 : 8;
const dir = new THREE.Vector3();
camera.getWorldDirection(dir);
dir.y = 0; dir.normalize();
const side = new THREE.Vector3().crossVectors(camera.up, dir).normalize();
if (move.forward) camera.position.addScaledVector(dir, speed * delta);
if (move.backward) camera.position.addScaledVector(dir, -speed * delta);
if (move.left) camera.position.addScaledVector(side, speed * delta);
if (move.right) camera.position.addScaledVector(side, -speed * delta);
if (isFlying) {
if (move.up) camera.position.y += speed * delta;
if (move.down) camera.position.y -= speed * delta;
} else {
velocity.y -= 9.8 * 0.05 * delta;
camera.position.y += velocity.y;
if (camera.position.y < 2) { camera.position.y = 2; velocity.y = 0; }
}
document.getElementById('val-c').innerText = `${camera.position.x.toFixed(1)}, ${camera.position.y.toFixed(1)}, ${camera.position.z.toFixed(1)}`;
document.getElementById('val-sp').innerText = (speed).toFixed(0);
if (isMining) {
raycaster.setFromCamera({x:0, y:0}, camera);
const intersects = raycaster.intersectObjects(objects);
if (intersects.length > 0 && intersects[0].object.position.y > 0) {
const target = intersects[0].object;
if (mineTarget !== target) { mineTarget = target; mineProgress = 0; }
mineProgress += delta * 4;
document.getElementById('mining-progress-container').style.display = 'block';
document.getElementById('mining-progress-bar').style.width = (mineProgress * 100) + '%';
if (mineProgress >= 1) {
scene.remove(target);
objects.splice(objects.indexOf(target), 1);
isMining = false;
document.getElementById('mining-progress-container').style.display = 'none';
}
}
}
}
mobs.forEach(m => m.update(delta));
renderer.render(scene, camera);
prevTime = time;
}
window.onload = () => { camera.position.set(0, 2, 5); animate(); };
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
window.addEventListener('contextmenu', e => e.preventDefault());
</script>
</body>
</html>