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">
<title>Advanced JS Minecraft</title>
<style>
body { margin: 0; overflow: hidden; font-family: sans-serif; }
#crosshair {
position: absolute; top: 50%; left: 50%;
width: 10px; height: 10px;
transform: translate(-50%, -50%);
color: white; font-weight: bold; font-size: 20px;
pointer-events: none; user-select: none; z-index: 10;
}
#ui-container {
position: absolute; top: 10px; left: 10px;
color: white; background: rgba(0,0,0,0.6);
padding: 15px; border-radius: 8px; pointer-events: none;
}
#hotbar {
position: absolute; bottom: 20px; left: 50%;
transform: translateX(-50%);
display: flex; background: rgba(0,0,0,0.6);
padding: 8px; border-radius: 5px; gap: 5px;
}
.slot {
width: 50px; height: 50px; border: 3px solid #555;
display: flex; flex-direction: column; align-items: center; justify-content: center;
color: white; font-size: 10px; font-weight: bold; text-align: center;
border-radius: 4px; background-size: cover;
}
.slot.active { border-color: #fff; background-color: rgba(255,255,255,0.3); }
</style>
</head>
<body>
<div id="crosshair">+</div>
<div id="ui-container">
<strong>Controls:</strong><br>
Click screen to lock mouse<br>
WASD = Move | Space = Jump<br>
Left Click = Destroy | Right Click = Place<br>
Keys 1 - 5 = Select Hotbar Slot
</div>
<div id="hotbar">
<div class="slot active" id="slot1" style="background-color: #557a2b;">Grass<br>[1]</div>
<div class="slot" id="slot2" style="background-color: #777777;">Stone<br>[2]</div>
<div class="slot" id="slot3" style="background-color: #d14915;">Lava<br>[3]</div>
<div class="slot" id="slot4" style="background-color: #2c2d30;">Netherite<br>[4]</div>
<div class="slot" id="slot5" style="background-color: #ffc0cb; color: black;">Spawn Animal<br>[5]</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/PointerLockControls.js"></script>
<script>
// 1. Scene 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);
// Lights
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
const sunLight = new THREE.DirectionalLight(0xffffff, 0.7);
sunLight.position.set(20, 40, 20);
scene.add(sunLight);
// 2. Inventory & Block Types System
const BLOCK_TYPES = {
1: { name: 'Grass', color: '#557a2b', isMob: false },
2: { name: 'Stone', color: '#777777', isMob: false },
3: { name: 'Lava', color: '#d14915', isMob: false },
4: { name: 'Netherite', color: '#2c2d30', isMob: false },
5: { name: 'Animal', color: null, isMob: true }
};
let activeSlot = 1;
// Visual grid textures
function createBlockMaterial(color) {
const canvas = document.createElement('canvas');
canvas.width = 16; canvas.height = 16;
const ctx = canvas.getContext('2d');
ctx.fillStyle = color; ctx.fillRect(0, 0, 16, 16);
ctx.strokeStyle = 'rgba(0,0,0,0.2)'; ctx.lineWidth = 1; ctx.strokeRect(0, 0, 16, 16);
return new THREE.MeshStandardMaterial({ map: new THREE.CanvasTexture(canvas), roughness: 0.8 });
}
const materials = {
1: createBlockMaterial('#557a2b'),
2: createBlockMaterial('#777777'),
3: new THREE.MeshStandardMaterial({ color: 0xd14915, emissive: 0x5a1800, roughness: 0.2 }), // Glowing Lava
4: createBlockMaterial('#2c2d30')
};
const geometry = new THREE.BoxGeometry(1, 1, 1);
const blocks = [];
const animals = [];
// Build flat starter terrain map
for (let x = 0; x < 20; x++) {
for (let z = 0; z < 20; z++) {
const mat = (x === 0 || x === 19 || z === 0 || z === 19) ? materials[2] : materials[1]; // stone border
const block = new THREE.Mesh(geometry, mat);
block.position.set(x, 0, z);
scene.add(block);
blocks.push(block);
}
}
// 3. Animals (Mobs) System
function spawnAnimal(x, y, z) {
const isPig = Math.random() > 0.5;
const animalGroup = new THREE.Group();
// Body
const bodyMat = new THREE.MeshStandardMaterial({ color: isPig ? 0xffb6c1 : 0xffffff }); // Pink Pig or White Sheep
const body = new THREE.Mesh(new THREE.BoxGeometry(0.6, 0.6, 0.9), bodyMat);
body.position.y = 0.4;
animalGroup.add(body);
// Head
const head = new THREE.Mesh(new THREE.BoxGeometry(0.4, 0.4, 0.4), bodyMat);
head.position.set(0, 0.6, 0.5);
animalGroup.add(head);
animalGroup.position.set(x, y, z);
scene.add(animalGroup);
animals.push({
mesh: animalGroup,
velocity: new THREE.Vector3(),
directionTimer: 0,
moveX: 0,
moveZ: 0
});
}
// Spawn a couple starter animals
spawnAnimal(5, 1, 5);
spawnAnimal(10, 1, 10);
// 4. Controls & Movement Engine
const controls = new THREE.PointerLockControls(camera, document.body);
document.body.addEventListener('click', () => controls.lock());
let moveForward = false, moveBackward = false, moveLeft = false, moveRight = false;
let playerVelocity = new THREE.Vector3();
let playerDirection = new THREE.Vector3();
let canJump = false;
document.addEventListener('keydown', (e) => {
if (/^[1-5]$/.test(e.key)) {
document.getElementById(`slot${activeSlot}`).classList.remove('active');
activeSlot = parseInt(e.key);
document.getElementById(`slot${activeSlot}`).classList.add('active');
}
switch (e.code) {
case 'KeyW': moveForward = true; break;
case 'KeyS': moveBackward = true; break;
case 'KeyA': moveLeft = true; break;
case 'KeyD': moveRight = true; break;
case 'Space': if (canJump) playerVelocity.y += 8; canJump = false; break;
}
});
document.addEventListener('keyup', (e) => {
switch (e.code) {
case 'KeyW': moveForward = false; break;
case 'KeyS': moveBackward = false; break;
case 'KeyA': moveLeft = false; break;
case 'KeyD': moveRight = false; break;
}
});
camera.position.set(10, 3, 10);
// 5. Build / Destroy Interaction
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2(0, 0);
document.addEventListener('pointerdown', (e) => {
if (!controls.isLocked) return;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(blocks);
if (intersects.length > 0 && intersects[0].distance < 7) {
const intersect = intersects[0];
if (e.button === 0) { // Left Click: Destroy
scene.remove(intersect.object);
blocks.splice(blocks.indexOf(intersect.object), 1);
}
else if (e.button === 2) { // Right Click: Place Block OR Spawn Mob
const spawnPos = intersect.object.position.clone().add(intersect.face.normal);
const currentSelection = BLOCK_TYPES[activeSlot];
if (currentSelection.isMob) {
spawnAnimal(spawnPos.x, spawnPos.y, spawnPos.z);
} else {
const newBlock = new THREE.Mesh(geometry, materials[activeSlot]);
newBlock.position.copy(spawnPos);
scene.add(newBlock);
blocks.push(newBlock);
}
}
}
});
document.addEventListener('contextmenu', e => e.preventDefault());
// 6. Physics and Game Loop Animation
const clock = new THREE.Clock();
const gravity = 20;
function animate() {
requestAnimationFrame(animate);
const delta = Math.min(clock.getDelta(), 0.1); // cap delta to prevent clipping glitches
if (controls.isLocked) {
// Player Physics
playerVelocity.x -= playerVelocity.x * 10.0 * delta;
playerVelocity.z -= playerVelocity.z * 10.0 * delta;
playerVelocity.y -= gravity * delta;
playerDirection.z = Number(moveForward) - Number(moveBackward);
playerDirection.x = Number(moveRight) - Number(moveLeft);
playerDirection.normalize();
if (moveForward || moveBackward) playerVelocity.z -= playerDirection.z * 40.0 * delta;
if (moveLeft || moveRight) playerVelocity.x -= playerDirection.x * 40.0 * delta;
controls.moveRight(-playerVelocity.x * delta);
controls.moveForward(-playerVelocity.z * delta);
camera.position.y += playerVelocity.y * delta;
// Ground Collision
if (camera.position.y < 2) {
playerVelocity.y = 0;
camera.position.y = 2;
canJump = true;
}
}
// AI Animal Logic
animals.forEach(animal => {
animal.directionTimer -= delta;
if (animal.directionTimer <= 0) {
// Pick a random direction or idle
animal.moveX = (Math.random() - 0.5) * 2;
animal.moveZ = (Math.random() - 0.5) * 2;
animal.directionTimer = Math.random() * 3 + 1; // Change minds every 1-4 seconds
// Random chance to jump
if(Math.random() > 0.6 && animal.mesh.position.y <= 0.1) {
animal.velocity.y = 5;
}
}
// Apply gravity to animal
animal.velocity.y -= gravity * delta;
animal.mesh.position.y += animal.velocity.y * delta;
// Simple flat world boundaries for animals
if (animal.mesh.position.y < 0) {
animal.mesh.position.y = 0;
animal.velocity.y = 0;
}
// Move animal smoothly
animal.mesh.position.x += animal.moveX * delta;
animal.mesh.position.z += animal.moveZ * delta;
// Make animal face the direction it's walking
if(Math.abs(animal.moveX) > 0.1 || Math.abs(animal.moveZ) > 0.1) {
animal.mesh.rotation.y = Math.atan2(animal.moveX, animal.moveZ);
}
});
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>
3
3
133KB
605KB
3,212.0ms
112.0ms
3,212.0ms