Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Simple City Simulation</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<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/controls/OrbitControls.min.js"></script> | |
<style> | |
body { | |
margin: 0; | |
overflow: hidden; | |
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
} | |
#info { | |
position: absolute; | |
top: 20px; | |
left: 20px; | |
background: rgba(0,0,0,0.7); | |
color: white; | |
padding: 15px; | |
border-radius: 10px; | |
max-width: 300px; | |
} | |
#controls { | |
position: absolute; | |
bottom: 20px; | |
left: 20px; | |
background: rgba(0,0,0,0.7); | |
color: white; | |
padding: 15px; | |
border-radius: 10px; | |
} | |
button { | |
background: #4f46e5; | |
color: white; | |
border: none; | |
padding: 8px 15px; | |
margin: 5px; | |
border-radius: 5px; | |
cursor: pointer; | |
transition: all 0.3s; | |
} | |
button:hover { | |
background: #6366f1; | |
} | |
.building { | |
transition: all 0.5s; | |
} | |
.building:hover { | |
transform: translateY(-5px); | |
box-shadow: 0 10px 20px rgba(0,0,0,0.3); | |
} | |
</style> | |
</head> | |
<body class="bg-gray-900"> | |
<div id="info"> | |
<h1 class="text-xl font-bold mb-2">Simple City Simulation</h1> | |
<p>Click and drag to rotate the view. Scroll to zoom in/out.</p> | |
<p class="mt-2 text-sm text-gray-300">Buildings: <span id="buildingCount">0</span></p> | |
</div> | |
<div id="controls"> | |
<button id="addBuilding">Add Building</button> | |
<button id="removeBuilding">Remove Building</button> | |
<button id="toggleLights">Toggle Lights</button> | |
<button id="dayNight">Day/Night</button> | |
</div> | |
<script> | |
// Initialize Three.js scene | |
const scene = new THREE.Scene(); | |
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); | |
renderer.setClearColor(0x87ceeb); // Sky blue | |
document.body.appendChild(renderer.domElement); | |
// Lighting | |
const ambientLight = new THREE.AmbientLight(0x404040); | |
scene.add(ambientLight); | |
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); | |
directionalLight.position.set(1, 1, 1); | |
scene.add(directionalLight); | |
// Grid helper | |
const gridHelper = new THREE.GridHelper(100, 100); | |
scene.add(gridHelper); | |
// City variables | |
let buildings = []; | |
let isNight = false; | |
let lightsOn = true; | |
// Create ground | |
const groundGeometry = new THREE.PlaneGeometry(100, 100); | |
const groundMaterial = new THREE.MeshStandardMaterial({ | |
color: 0x333333, | |
roughness: 0.8, | |
metalness: 0.2 | |
}); | |
const ground = new THREE.Mesh(groundGeometry, groundMaterial); | |
ground.rotation.x = -Math.PI / 2; | |
scene.add(ground); | |
// Create roads | |
function createRoads() { | |
const roadMaterial = new THREE.MeshStandardMaterial({ color: 0x555555 }); | |
// Main roads | |
for (let i = -40; i <= 40; i += 20) { | |
// Horizontal roads | |
const hRoadGeometry = new THREE.PlaneGeometry(90, 5); | |
const hRoad = new THREE.Mesh(hRoadGeometry, roadMaterial); | |
hRoad.position.set(0, 0.01, i); | |
hRoad.rotation.x = -Math.PI / 2; | |
scene.add(hRoad); | |
// Vertical roads | |
const vRoadGeometry = new THREE.PlaneGeometry(5, 90); | |
const vRoad = new THREE.Mesh(vRoadGeometry, roadMaterial); | |
vRoad.position.set(i, 0.01, 0); | |
vRoad.rotation.x = -Math.PI / 2; | |
scene.add(vRoad); | |
} | |
// Road markings | |
const lineMaterial = new THREE.LineBasicMaterial({ color: 0xffff00 }); | |
for (let i = -40; i <= 40; i += 20) { | |
// Horizontal lines | |
for (let j = -45; j <= 45; j += 5) { | |
const points = []; | |
points.push(new THREE.Vector3(j, 0.02, i - 2)); | |
points.push(new THREE.Vector3(j, 0.02, i + 2)); | |
const hLineGeometry = new THREE.BufferGeometry().setFromPoints(points); | |
const hLine = new THREE.Line(hLineGeometry, lineMaterial); | |
scene.add(hLine); | |
} | |
// Vertical lines | |
for (let j = -45; j <= 45; j += 5) { | |
const points = []; | |
points.push(new THREE.Vector3(i - 2, 0.02, j)); | |
points.push(new THREE.Vector3(i + 2, 0.02, j)); | |
const vLineGeometry = new THREE.BufferGeometry().setFromPoints(points); | |
const vLine = new THREE.Line(vLineGeometry, lineMaterial); | |
scene.add(vLine); | |
} | |
} | |
} | |
createRoads(); | |
// Create random building | |
function createBuilding(x, z) { | |
const width = 3 + Math.random() * 4; | |
const depth = 3 + Math.random() * 4; | |
const height = 5 + Math.random() * 15; | |
// Random color | |
const colors = [0x8a2be2, 0x4682b4, 0x556b2f, 0xb8860b, 0xcd5c5c, 0x9370db]; | |
const color = colors[Math.floor(Math.random() * colors.length)]; | |
const geometry = new THREE.BoxGeometry(width, height, depth); | |
const material = new THREE.MeshStandardMaterial({ | |
color: color, | |
roughness: 0.7, | |
metalness: 0.1 | |
}); | |
const building = new THREE.Mesh(geometry, material); | |
building.position.set(x, height / 2, z); | |
building.castShadow = true; | |
building.receiveShadow = true; | |
building.className = 'building'; | |
// Add windows | |
if (lightsOn) { | |
const windowColor = isNight ? 0xffffaa : 0xadd8e6; | |
const windowMaterial = new THREE.MeshBasicMaterial({ color: windowColor }); | |
const windowWidth = width * 0.8; | |
const windowHeight = height * 0.8; | |
const windowDepth = depth * 0.8; | |
const windowGeometry = new THREE.BoxGeometry(windowWidth, windowHeight, windowDepth); | |
const windowMesh = new THREE.Mesh(windowGeometry, windowMaterial); | |
windowMesh.position.set(0, 0, 0.1); | |
building.add(windowMesh); | |
} | |
scene.add(building); | |
buildings.push(building); | |
updateBuildingCount(); | |
return building; | |
} | |
// Generate initial city | |
function generateCity() { | |
// Clear existing buildings | |
buildings.forEach(building => scene.remove(building)); | |
buildings = []; | |
// Create buildings in blocks | |
for (let x = -35; x <= 35; x += 10) { | |
for (let z = -35; z <= 35; z += 10) { | |
// Skip center blocks to leave space for roads | |
if (Math.abs(x) < 10 && Math.abs(z) < 10) continue; | |
// Create 1-3 buildings per block | |
const buildingsInBlock = 1 + Math.floor(Math.random() * 3); | |
for (let i = 0; i < buildingsInBlock; i++) { | |
const offsetX = (Math.random() - 0.5) * 6; | |
const offsetZ = (Math.random() - 0.5) * 6; | |
createBuilding(x + offsetX, z + offsetZ); | |
} | |
} | |
} | |
} | |
generateCity(); | |
// Update building count display | |
function updateBuildingCount() { | |
document.getElementById('buildingCount').textContent = buildings.length; | |
} | |
// Toggle day/night | |
function toggleDayNight() { | |
isNight = !isNight; | |
if (isNight) { | |
renderer.setClearColor(0x000033); | |
ambientLight.intensity = 0.2; | |
directionalLight.intensity = 0.3; | |
} else { | |
renderer.setClearColor(0x87ceeb); | |
ambientLight.intensity = 0.4; | |
directionalLight.intensity = 0.8; | |
} | |
// Update building windows | |
buildings.forEach(building => { | |
const windowMesh = building.children[0]; | |
if (windowMesh) { | |
windowMesh.material.color.setHex(isNight ? 0xffffaa : 0xadd8e6); | |
} | |
}); | |
} | |
// Toggle lights | |
function toggleLights() { | |
lightsOn = !lightsOn; | |
buildings.forEach(building => { | |
if (lightsOn) { | |
const windowColor = isNight ? 0xffffaa : 0xadd8e6; | |
const windowMaterial = new THREE.MeshBasicMaterial({ color: windowColor }); | |
const width = building.geometry.parameters.width * 0.8; | |
const height = building.geometry.parameters.height * 0.8; | |
const depth = building.geometry.parameters.depth * 0.8; | |
const windowGeometry = new THREE.BoxGeometry(width, height, depth); | |
const windowMesh = new THREE.Mesh(windowGeometry, windowMaterial); | |
windowMesh.position.set(0, 0, 0.1); | |
building.add(windowMesh); | |
} else { | |
if (building.children.length > 0) { | |
building.remove(building.children[0]); | |
} | |
} | |
}); | |
} | |
// Camera position | |
camera.position.set(40, 40, 40); | |
camera.lookAt(0, 0, 0); | |
// Orbit controls | |
const controls = new THREE.OrbitControls(camera, renderer.domElement); | |
controls.enableDamping = true; | |
controls.dampingFactor = 0.05; | |
// Event listeners for buttons | |
document.getElementById('addBuilding').addEventListener('click', () => { | |
const x = (Math.random() - 0.5) * 80; | |
const z = (Math.random() - 0.5) * 80; | |
createBuilding(x, z); | |
}); | |
document.getElementById('removeBuilding').addEventListener('click', () => { | |
if (buildings.length > 0) { | |
const building = buildings.pop(); | |
scene.remove(building); | |
updateBuildingCount(); | |
} | |
}); | |
document.getElementById('toggleLights').addEventListener('click', toggleLights); | |
document.getElementById('dayNight').addEventListener('click', toggleDayNight); | |
// Handle window resize | |
window.addEventListener('resize', () => { | |
camera.aspect = window.innerWidth / window.innerHeight; | |
camera.updateProjectionMatrix(); | |
renderer.setSize(window.innerWidth, window.innerHeight); | |
}); | |
// Animation loop | |
function animate() { | |
requestAnimationFrame(animate); | |
controls.update(); | |
renderer.render(scene, camera); | |
} | |
animate(); | |
</script> | |
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=MakiAi/simcity-demo-001" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
</html> |