Spaces:
Running
Running
<html> | |
<head> | |
<title>3D Flying Game</title> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/aframe/1.4.2/aframe.min.js"></script> | |
</head> | |
<body> | |
<a-scene> | |
<!-- Assets --> | |
<a-assets> | |
<img id="ground-tile" src="/api/placeholder/128/128" /> | |
<img id="bump-map" src="/api/placeholder/128/128" /> | |
<!-- Custom SVG shapes --> | |
<svg id="crystal" viewBox="0 0 100 100"> | |
<path d="M50 0 L100 25 L100 75 L50 100 L0 75 L0 25 Z" fill="#88ff88"/> | |
</svg> | |
<svg id="starShape" viewBox="0 0 100 100"> | |
<path d="M50 0 L63 38 L100 38 L69 59 L81 100 L50 75 L19 100 L31 59 L0 38 L37 38 Z" fill="#ffff00"/> | |
</svg> | |
</a-assets> | |
<!-- Environment --> | |
<a-sky color="#001133"></a-sky> | |
<a-entity id="world"> | |
<a-entity id="tileMap"></a-entity> | |
<a-entity id="decorativeShapes"></a-entity> | |
</a-entity> | |
<!-- Aircraft --> | |
<a-entity id="aircraft" position="0 10 0"> | |
<!-- Main body --> | |
<a-box width="2" height="0.5" depth="4" color="#ff3333" metalness="0.8" roughness="0.2" shadow></a-box> | |
<!-- Wings --> | |
<a-box width="8" height="0.2" depth="1.5" position="0 0 0" color="#cc2222" metalness="0.8" roughness="0.2" shadow></a-box> | |
<!-- Tail --> | |
<a-box width="1" height="1" depth="0.2" position="0 0.5 -1.8" color="#cc2222" metalness="0.8" roughness="0.2" shadow></a-box> | |
<!-- Nose cone --> | |
<a-cone radius-bottom="0.8" radius-top="0.1" height="2" position="0 0 2.5" rotation="90 0 0" color="#cc2222" metalness="0.8" roughness="0.2"></a-cone> | |
<!-- Engine glow --> | |
<a-sphere radius="0.3" position="0 -0.2 -2" color="#ffaa44" emission="intensity: 1"></a-sphere> | |
</a-entity> | |
<!-- Camera --> | |
<a-entity id="rig"> | |
<a-camera position="0 2 12" look-controls wasd-controls="enabled: false"></a-camera> | |
</a-entity> | |
<!-- Lighting --> | |
<a-light type="ambient" color="#445566"></a-light> | |
<a-light type="directional" position="2 4 1" color="#ffffff" intensity="0.8" cast-shadow="true"></a-light> | |
</a-scene> | |
<script> | |
// Aircraft state | |
const aircraftState = { | |
position: { x: 0, y: 10, z: 0 }, | |
rotation: { x: 0, y: 0, z: 0 }, | |
velocity: { x: 0, y: 0, z: 0 }, | |
maxSpeed: 1.0, | |
acceleration: 0.05, | |
turnSpeed: 2, | |
bankAngle: 0, | |
maxBankAngle: 45 | |
}; | |
// World generation parameters | |
const worldState = { | |
chunkSize: 20, | |
tileSize: 5, | |
visibleChunks: 3, | |
currentChunk: { x: 0, z: 0 } | |
}; | |
const colors = ['#2244aa', '#22aa44', '#aa2244', '#aaaa22']; | |
// Generate a single chunk of terrain | |
function generateChunk(chunkX, chunkZ) { | |
const chunk = document.createElement('a-entity'); | |
const startX = chunkX * worldState.chunkSize * worldState.tileSize; | |
const startZ = chunkZ * worldState.chunkSize * worldState.tileSize; | |
for (let x = 0; x < worldState.chunkSize; x++) { | |
for (let z = 0; z < worldState.chunkSize; z++) { | |
const tile = document.createElement('a-box'); | |
const worldX = startX + x * worldState.tileSize; | |
const worldZ = startZ + z * worldState.tileSize; | |
tile.setAttribute('width', worldState.tileSize); | |
tile.setAttribute('height', 0.1); | |
tile.setAttribute('depth', worldState.tileSize); | |
tile.setAttribute('position', { | |
x: worldX, | |
y: -0.05, | |
z: worldZ | |
}); | |
tile.setAttribute('material', { | |
color: colors[((x + z) + (chunkX + chunkZ)) % colors.length], | |
src: '#ground-tile', | |
repeat: { x: 1, y: 1 }, | |
normalMap: '#bump-map' | |
}); | |
tile.setAttribute('shadow', ''); | |
chunk.appendChild(tile); | |
// Add random decorative shapes | |
if (Math.random() < 0.05) { | |
const shape = document.createElement('a-entity'); | |
const type = Math.floor(Math.random() * 2); | |
const scale = 2 + Math.random() * 3; | |
const height = Math.random() * 10; | |
shape.setAttribute('position', `${worldX} ${height} ${worldZ}`); | |
shape.setAttribute('rotation', `0 ${Math.random() * 360} 0`); | |
shape.setAttribute('scale', `${scale} ${scale} ${scale}`); | |
if (type === 0) { | |
shape.setAttribute('geometry', 'primitive: cylinder; radius: 0.5; height: 1;'); | |
shape.setAttribute('material', 'color: #ff88ff; metalness: 0.5; roughness: 0.2'); | |
} else { | |
shape.setAttribute('geometry', 'primitive: cone; radiusBottom: 0.7; radiusTop: 0;'); | |
shape.setAttribute('material', 'color: #ffff88; metalness: 0.5; roughness: 0.2'); | |
} | |
chunk.appendChild(shape); | |
} | |
} | |
} | |
return chunk; | |
} | |
// Update visible chunks based on aircraft position | |
function updateWorld() { | |
const currentChunkX = Math.floor(aircraftState.position.x / (worldState.chunkSize * worldState.tileSize)); | |
const currentChunkZ = Math.floor(aircraftState.position.z / (worldState.chunkSize * worldState.tileSize)); | |
if (currentChunkX !== worldState.currentChunk.x || currentChunkZ !== worldState.currentChunk.z) { | |
const world = document.querySelector('#world'); | |
world.innerHTML = ''; | |
for (let x = -1; x <= 1; x++) { | |
for (let z = -1; z <= 1; z++) { | |
const chunk = generateChunk(currentChunkX + x, currentChunkZ + z); | |
world.appendChild(chunk); | |
} | |
} | |
worldState.currentChunk = { x: currentChunkX, z: currentChunkZ }; | |
} | |
} | |
// Controls | |
const aircraft = document.querySelector('#aircraft'); | |
const camera = document.querySelector('#rig'); | |
let keys = {}; | |
document.addEventListener('keydown', (e) => { | |
keys[e.key.toLowerCase()] = true; | |
}); | |
document.addEventListener('keyup', (e) => { | |
keys[e.key.toLowerCase()] = false; | |
}); | |
// Game loop | |
function updateGame() { | |
// Aircraft controls | |
if (keys['s']) { // Forward (swapped) | |
aircraftState.velocity.z -= aircraftState.acceleration; | |
} else if (keys['w']) { // Backward (swapped) | |
aircraftState.velocity.z += aircraftState.acceleration; | |
} | |
// Banking controls | |
if (keys['q']) { | |
aircraftState.bankAngle = Math.min(aircraftState.bankAngle + 2, aircraftState.maxBankAngle); | |
aircraftState.velocity.x -= aircraftState.acceleration * 0.5; | |
} else if (keys['e']) { | |
aircraftState.bankAngle = Math.max(aircraftState.bankAngle - 2, -aircraftState.maxBankAngle); | |
aircraftState.velocity.x += aircraftState.acceleration * 0.5; | |
} else { | |
aircraftState.bankAngle *= 0.95; // Return to level flight | |
} | |
// Turning | |
if (keys['a']) { | |
aircraftState.rotation.y += aircraftState.turnSpeed; | |
} | |
if (keys['d']) { | |
aircraftState.rotation.y -= aircraftState.turnSpeed; | |
} | |
// Apply physics | |
aircraftState.velocity.z *= 0.99; // Air resistance | |
aircraftState.velocity.x *= 0.99; | |
// Clamp velocity | |
aircraftState.velocity.z = Math.max(Math.min(aircraftState.velocity.z, aircraftState.maxSpeed), -aircraftState.maxSpeed); | |
aircraftState.velocity.x = Math.max(Math.min(aircraftState.velocity.x, aircraftState.maxSpeed), -aircraftState.maxSpeed); | |
// Update position | |
const rad = (aircraftState.rotation.y * Math.PI) / 180; | |
aircraftState.position.x += Math.sin(rad) * aircraftState.velocity.z + aircraftState.velocity.x; | |
aircraftState.position.z += Math.cos(rad) * aircraftState.velocity.z; | |
// Update aircraft position and rotation | |
aircraft.setAttribute('position', aircraftState.position); | |
aircraft.setAttribute('rotation', { | |
x: -aircraftState.velocity.z * 15, // Pitch based on speed | |
y: aircraftState.rotation.y, | |
z: aircraftState.bankAngle | |
}); | |
// Camera follow | |
const cameraDistance = 12; | |
const cameraHeight = 2; | |
camera.setAttribute('position', { | |
x: aircraftState.position.x - Math.sin(rad) * cameraDistance, | |
y: aircraftState.position.y + cameraHeight, | |
z: aircraftState.position.z - Math.cos(rad) * cameraDistance | |
}); | |
camera.setAttribute('rotation', { | |
x: 15, | |
y: aircraftState.rotation.y, | |
z: 0 | |
}); | |
// Update world chunks | |
updateWorld(); | |
requestAnimationFrame(updateGame); | |
} | |
// Start the game loop | |
updateGame(); | |
</script> | |
</body> | |
</html> |