grok-game / index.html
kadirnar's picture
Update index.html
07a5d04 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D Maze Game Demo</title>
<style>
body { margin: 0; overflow: hidden; }
#debug {
position: absolute;
top: 10px;
left: 10px;
color: white;
background: rgba(0,0,0,0.5);
padding: 5px;
font-family: monospace;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/loaders/GLTFLoader.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/controls/OrbitControls.js"></script>
</head>
<body>
<canvas id="gameCanvas"></canvas>
<div id="debug">Position: 0, 0, 0<br>Rotation: 0 degrees</div>
<script>
// Scene Setup
const canvas = document.getElementById('gameCanvas');
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ canvas: canvas });
renderer.setSize(window.innerWidth, window.innerHeight);
// OrbitControls
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.screenSpacePanning = false;
controls.minDistance = 5;
controls.maxDistance = 50;
controls.maxPolarAngle = Math.PI / 2;
// Lighting
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 0.6);
hemiLight.position.set(0, 20, 0);
scene.add(hemiLight);
const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
dirLight.position.set(3, 10, 3);
scene.add(dirLight);
// Floor Texture
function createFloorTexture() {
const canvas = document.createElement('canvas');
canvas.width = 64;
canvas.height = 64;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#8B4513'; // Brown
ctx.fillRect(0, 0, 64, 64);
ctx.strokeStyle = '#000000';
ctx.lineWidth = 2;
for (let i = 0; i <= 64; i += 4) {
ctx.moveTo(i, 0); ctx.lineTo(i, 64);
ctx.moveTo(0, i); ctx.lineTo(64, i);
}
ctx.stroke();
return canvas;
}
const floorTexture = new THREE.CanvasTexture(createFloorTexture());
floorTexture.wrapS = THREE.RepeatWrapping;
floorTexture.wrapT = THREE.RepeatWrapping;
floorTexture.repeat.set(16, 16);
const floorGeometry = new THREE.PlaneGeometry(64, 64);
const floorMaterial = new THREE.MeshLambertMaterial({ map: floorTexture });
const floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.rotation.x = -Math.PI / 2;
floor.position.set(32, 0, 32);
scene.add(floor);
// Wall Texture
function createWallTexture() {
const canvas = document.createElement('canvas');
canvas.width = 64;
canvas.height = 64;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#808080'; // Gray
ctx.fillRect(0, 0, 64, 64);
ctx.fillStyle = '#707070';
for (let i = 0; i < 100; i++) {
const x = Math.random() * 64;
const y = Math.random() * 64;
const size = Math.random() * 5;
ctx.fillRect(x, y, size, size);
}
return canvas;
}
const wallTexture = new THREE.CanvasTexture(createWallTexture());
const wallMaterial = new THREE.MeshLambertMaterial({ map: wallTexture });
// Maze Generation
const gridSize = 16;
const cellSize = 4;
const maze = [];
for (let i = 0; i < gridSize; i++) {
maze[i] = [];
for (let j = 0; j < gridSize; j++) {
if (i === 0 || i === gridSize - 1 || j === 0 || j === gridSize - 1) {
maze[i][j] = 1; // Boundary walls
} else if (i >= 6 && i <= 9 && j >= 6 && j <= 9) {
maze[i][j] = 0; // Central open area
} else {
maze[i][j] = Math.random() < 0.15 ? 1 : 0; // Random walls
}
}
}
// Wall Meshes and Collision Boxes
const wallBoxes = [];
for (let i = 0; i < gridSize; i++) {
for (let j = 0; j < gridSize; j++) {
if (maze[i][j] === 1) {
const wall = new THREE.Mesh(
new THREE.BoxGeometry(cellSize, cellSize, cellSize),
wallMaterial
);
wall.position.set((i + 0.5) * cellSize, cellSize / 2, (j + 0.5) * cellSize);
scene.add(wall);
wallBoxes.push(new THREE.Box3().setFromObject(wall));
}
}
}
// Soldier Setup
let soldier, mixer, idleAction, runAction, currentAction;
const loader = new THREE.GLTFLoader();
loader.load('https://threejs.org/examples/models/gltf/Soldier.glb', (gltf) => {
soldier = gltf.scene;
soldier.scale.set(2, 2, 2);
soldier.position.set(30, 0, 30); // Center of open area
scene.add(soldier);
mixer = new THREE.AnimationMixer(soldier);
const clips = gltf.animations;
idleAction = mixer.clipAction(clips.find(clip => clip.name === 'Idle'));
runAction = mixer.clipAction(clips.find(clip => clip.name === 'Run'));
idleAction.play();
currentAction = idleAction;
camera.position.set(30, 10, 40);
controls.target.copy(soldier.position);
controls.update();
animate();
});
// Keyboard Controls (WASD)
const keys = { w: false, a: false, s: false, d: false };
window.addEventListener('keydown', (e) => {
const key = e.key.toLowerCase();
if (keys.hasOwnProperty(key)) keys[key] = true;
});
window.addEventListener('keyup', (e) => {
const key = e.key.toLowerCase();
if (keys.hasOwnProperty(key)) keys[key] = false;
});
// Movement Logic
const clock = new THREE.Clock();
const speed = 5; // Movement speed in units per second
const collisionRadius = 1; // Collision sphere radius
function updateMovement(deltaTime) {
if (!soldier) return;
const forward = new THREE.Vector3();
camera.getWorldDirection(forward);
forward.y = 0;
forward.normalize();
const left = new THREE.Vector3(-forward.z, 0, forward.x);
const moveDirection = new THREE.Vector3();
if (keys.w) moveDirection.add(forward);
if (keys.s) moveDirection.add(forward.clone().negate());
if (keys.a) moveDirection.add(left);
if (keys.d) moveDirection.add(left.clone().negate());
if (moveDirection.length() > 0) {
moveDirection.normalize();
const newPosition = soldier.position.clone().add(
moveDirection.multiplyScalar(speed * deltaTime)
);
// Collision Detection
const soldierSphere = new THREE.Sphere(newPosition, collisionRadius);
let collision = false;
for (const wallBox of wallBoxes) {
if (wallBox.intersectsSphere(soldierSphere)) {
collision = true;
break;
}
}
if (!collision) {
soldier.position.copy(newPosition);
}
// Rotate soldier to face movement direction
const angle = Math.atan2(moveDirection.x, moveDirection.z);
soldier.rotation.y = angle + Math.PI;
// Switch to run animation
if (currentAction !== runAction) {
currentAction = runAction;
idleAction.fadeOut(0.2);
runAction.reset().fadeIn(0.2).play();
}
} else {
// Switch to idle animation
if (currentAction !== idleAction) {
currentAction = idleAction;
runAction.fadeOut(0.2);
idleAction.reset().fadeIn(0.2).play();
}
}
// Update camera target
controls.target.copy(soldier.position);
}
// Animation Loop
function animate() {
requestAnimationFrame(animate);
const deltaTime = clock.getDelta();
if (mixer) mixer.update(deltaTime);
updateMovement(deltaTime);
controls.update();
renderer.render(scene, camera);
// Debug Overlay
if (soldier) {
const pos = soldier.position;
const rot = ((soldier.rotation.y * 180 / Math.PI) % 360).toFixed(2);
document.getElementById('debug').innerHTML =
`Position: ${pos.x.toFixed(2)}, ${pos.y.toFixed(2)}, ${pos.z.toFixed(2)}<br>` +
`Rotation: ${rot} degrees`;
}
}
// Window Resize
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
</script>
</body>
</html>