Uo / script.js
yyy
Upload folder using huggingface_hub
063aef8 verified
Raw
History Blame Contribute Delete
9.47 kB
// Simple 3D engine using Canvas 2D with isometric projection
class Simple3DEngine {
constructor(canvas) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.width = canvas.width;
this.height = canvas.height;
this.camera = { x: 0, y: 0, z: 0, angle: 0, elevation: 0.5 };
this.objects = [];
this.keys = {};
this.buildingsDestroyed = 0;
this.debrisCount = 0;
// Set canvas size
this.resize();
window.addEventListener('resize', () => this.resize());
// Setup keyboard controls
document.addEventListener('keydown', (e) => this.keys[e.key.toLowerCase()] = true);
document.addEventListener('keyup', (e) => this.keys[e.key.toLowerCase()] = false);
// Start animation loop
this.lastTime = 0;
this.animate();
}
resize() {
this.canvas.width = this.canvas.clientWidth;
this.canvas.height = this.canvas.clientHeight;
this.width = this.canvas.width;
this.height = this.canvas.height;
}
// Isometric projection
project(x, y, z) {
const cosA = Math.cos(this.camera.angle);
const sinA = Math.sin(this.camera.angle);
// Rotate around Y axis
const x1 = x * cosA - z * sinA;
const z1 = x * sinA + z * cosA;
// Apply camera position
x1 -= this.camera.x;
y -= this.camera.y;
z1 -= this.camera.z;
// Isometric projection
const scale = 80 / (z1 + 10);
const screenX = this.width / 2 + (x1 * scale);
const screenY = this.height / 2 - (y * scale) + (z1 * scale * this.camera.elevation);
return { x: screenX, y: screenY, scale: scale };
}
// Draw a 3D box
drawBox(x, y, z, width, height, depth, color) {
const p1 = this.project(x, y, z);
const p2 = this.project(x + width, y, z);
const p3 = this.project(x + width, y + height, z);
const p4 = this.project(x, y + height, z);
const p5 = this.project(x, y, z + depth);
const p6 = this.project(x + width, y, z + depth);
const p7 = this.project(x + width, y + height, z + depth);
const p8 = this.project(x, y + height, z + depth);
// Only draw if in front of camera
if (p1.scale <= 0) return;
this.ctx.strokeStyle = color;
this.ctx.fillStyle = color + '80';
this.ctx.lineWidth = 2;
// Draw front face
this.ctx.beginPath();
this.ctx.moveTo(p1.x, p1.y);
this.ctx.lineTo(p2.x, p2.y);
this.ctx.lineTo(p3.x, p3.y);
this.ctx.lineTo(p4.x, p4.y);
this.ctx.closePath();
this.ctx.fill();
this.ctx.stroke();
// Draw top face
this.ctx.beginPath();
this.ctx.moveTo(p1.x, p1.y);
this.ctx.lineTo(p2.x, p2.y);
this.ctx.lineTo(p6.x, p6.y);
this.ctx.lineTo(p5.x, p5.y);
this.ctx.closePath();
this.ctx.fill();
this.ctx.stroke();
// Draw side face
this.ctx.beginPath();
this.ctx.moveTo(p2.x, p2.y);
this.ctx.lineTo(p3.x, p3.y);
this.ctx.lineTo(p7.x, p7.y);
this.ctx.lineTo(p6.x, p6.y);
this.ctx.closePath();
this.ctx.fill();
this.ctx.stroke();
}
// Draw the bulldozer
drawBulldozer(x, y, z) {
// Main body
this.drawBox(x - 1, y - 0.5, z - 1.5, 2, 1, 3, '#4a5568');
// Blade
this.drawBox(x - 1.5, y - 0.3, z - 2, 3, 0.6, 0.3, '#e53e3e');
// Cab
this.drawBox(x - 0.8, y - 0.5, z + 0.5, 1.6, 1.2, 1.2, '#2d3748');
// Wheels
this.drawBox(x - 0.9, y - 0.5, z - 1, 0.3, 0.3, 0.3, '#2d3748');
this.drawBox(x + 0.6, y - 0.5, z - 1, 0.3, 0.3, 0.3, '#2d3748');
this.drawBox(x - 0.9, y - 0.5, z + 0.5, 0.3, 0.3, 0.3, '#2d3748');
this.drawBox(x + 0.6, y - 0.5, z + 0.5, 0.3, 0.3, 0.3, '#2d3748');
}
// Draw debris
drawDebris(x, y, z, size) {
this.drawBox(x, y, z, size, size, size, '#a0aec0');
}
// Create initial scene
createScene() {
this.objects = [];
this.buildingsDestroyed = 0;
this.debrisCount = 0;
// Create buildings
for (let i = 0; i < 5; i++) {
for (let j = 0; j < 5; j++) {
if (i === 2 && j === 2) continue; // Skip center for bulldozer
this.objects.push({
type: 'building',
x: (i - 2) * 8,
y: 0,
z: (j - 2) * 8,
width: 3 + Math.random() * 2,
height: 4 + Math.random() * 6,
depth: 3 + Math.random() * 2,
color: `hsl(${200 + Math.random() * 40}, 50%, 50%)`,
destroyed: false
});
}
}
// Create ground
this.ground = {
x: -25, y: -0.1, z: -25,
width: 50, depth: 50
};
}
// Update game state
update(deltaTime) {
// Handle bulldozer movement
const moveSpeed = deltaTime * 0.1;
if (this.keys['w']) this.camera.z -= moveSpeed;
if (this.keys['s']) this.camera.z += moveSpeed;
if (this.keys['a']) this.camera.x -= moveSpeed;
if (this.keys['d']) this.camera.x += moveSpeed;
// Handle camera rotation
if (this.keys['arrowleft']) this.camera.angle -= deltaTime * 0.002;
if (this.keys['arrowright']) this.camera.angle += deltaTime * 0.002;
// Reset scene
if (this.keys['r']) {
this.camera = { x: 0, y: 0, z: 0, angle: 0, elevation: 0.5 };
this.createScene();
}
// Check collisions with buildings
const bulldozerX = this.camera.x;
const bulldozerZ = this.camera.z;
this.objects.forEach(obj => {
if (obj.type === 'building' && !obj.destroyed) {
const dx = bulldozerX - obj.x;
const dz = bulldozerZ - obj.z;
const distance = Math.sqrt(dx * dx + dz * dz);
if (distance < 3) {
obj.destroyed = true;
this.buildingsDestroyed++;
// Create debris
for (let i = 0; i < 10; i++) {
this.objects.push({
type: 'debris',
x: obj.x + (Math.random() - 0.5) * obj.width,
y: obj.y + Math.random() * obj.height,
z: obj.z + (Math.random() - 0.5) * obj.depth,
size: 0.2 + Math.random() * 0.3
});
this.debrisCount++;
}
}
}
});
// Update stats display
document.getElementById('buildings-destroyed').textContent = this.buildingsDestroyed;
document.getElementById('debris-count').textContent = this.debrisCount;
}
// Render the scene
render() {
this.ctx.clearRect(0, 0, this.width, this.height);
// Draw ground
this.ctx.fillStyle = '#2d3748';
this.ctx.fillRect(0, 0, this.width, this.height);
// Draw grid on ground
this.ctx.strokeStyle = '#4a5568';
this.ctx.lineWidth = 1;
for (let i = -10; i <= 10; i++) {
const p1 = this.project(i * 5, 0, -50);
const p2 = this.project(i * 5, 0, 50);
this.ctx.beginPath();
this.ctx.moveTo(p1.x, p1.y);
this.ctx.lineTo(p2.x, p2.y);
this.ctx.stroke();
const p3 = this.project(-50, 0, i * 5);
const p4 = this.project(50, 0, i * 5);
this.ctx.beginPath();
this.ctx.moveTo(p3.x, p3.y);
this.ctx.lineTo(p4.x, p4.y);
this.ctx.stroke();
}
// Sort objects by depth for proper rendering
const sortedObjects = [...this.objects].sort((a, b) => {
const aDepth = a.z + (a.depth || 0);
const bDepth = b.z + (b.depth || 0);
return bDepth - aDepth;
});
// Draw objects
sortedObjects.forEach(obj => {
if (obj.type === 'building' && !obj.destroyed) {
this.drawBox(obj.x, obj.y, obj.z, obj.width, obj.height, obj.depth, obj.color);
} else if (obj.type === 'debris') {
this.drawDebris(obj.x, obj.y, obj.z, obj.size);
}
});
// Draw bulldozer at camera position
this.drawBulldozer(this.camera.x, this.camera.y, this.camera.z);
}
// Animation loop
animate = (timestamp) => {
const deltaTime = timestamp - this.lastTime;
this.lastTime = timestamp;
this.update(deltaTime);
this.render();
requestAnimationFrame(this.animate);
}
}
// Initialize the game when the page loads
window.addEventListener('load', () => {
const canvas = document.getElementById('gameCanvas');
const engine = new Simple3DEngine(canvas);
engine.createScene();
});