|
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<title>Tower Building Game</title> |
|
<meta charset="utf-8"> |
|
<style> |
|
body { |
|
margin: 0; |
|
overflow: hidden; |
|
} |
|
canvas { |
|
display: block; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> |
|
<script src="https://cdn.jsdelivr.net/npm/cannon/build/cannon.min.js"></script> |
|
<script> |
|
let scene, camera, renderer; |
|
let world, stack; |
|
let blocks = []; |
|
let currentBlock = null; |
|
let score = 0; |
|
|
|
init(); |
|
animate(); |
|
|
|
function init() { |
|
|
|
scene = new THREE.Scene(); |
|
|
|
|
|
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); |
|
camera.position.set(0, 50, 100); |
|
camera.lookAt(0, 0, 0); |
|
|
|
|
|
renderer = new THREE.WebGLRenderer({ antialias: true }); |
|
renderer.setSize(window.innerWidth, window.innerHeight); |
|
document.body.appendChild(renderer.domElement); |
|
|
|
|
|
let ambientLight = new THREE.AmbientLight(0xffffff, 0.5); |
|
scene.add(ambientLight); |
|
|
|
let directionalLight = new THREE.DirectionalLight(0xffffff, 0.5); |
|
directionalLight.position.set(0, 100, 100); |
|
scene.add(directionalLight); |
|
|
|
|
|
let planeGeometry = new THREE.PlaneGeometry(100, 100); |
|
let planeMaterial = new THREE.MeshPhongMaterial({ color: 0xffffff }); |
|
let plane = new THREE.Mesh(planeGeometry, planeMaterial); |
|
plane.rotation.x = -Math.PI / 2; |
|
scene.add(plane); |
|
|
|
|
|
world = new CANNON.World(); |
|
world.gravity.set(0, -9.82, 0); |
|
world.broadphase = new CANNON.NaiveBroadphase(); |
|
world.solver.iterations = 10; |
|
|
|
|
|
let groundShape = new CANNON.Plane(); |
|
let groundBody = new CANNON.Body({ mass: 0 }); |
|
groundBody.addShape(groundShape); |
|
groundBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2); |
|
world.addBody(groundBody); |
|
|
|
|
|
document.addEventListener('keydown', onKeyDown, false); |
|
} |
|
|
|
function animate() { |
|
requestAnimationFrame(animate); |
|
|
|
|
|
world.step(1 / 60); |
|
|
|
|
|
for (let i = 0; i < blocks.length; i++) { |
|
let block = blocks[i]; |
|
let body = block.userData.physicsBody; |
|
block.position.copy(body.position); |
|
block.quaternion.copy(body.quaternion); |
|
} |
|
|
|
|
|
renderer.render(scene, camera); |
|
} |
|
|
|
function createBlock() { |
|
|
|
let blockGeometry = new THREE.BoxGeometry(10, 10, 10); |
|
let blockMaterial = new THREE.MeshPhongMaterial({ map: new THREE.TextureLoader().load('block_texture.jpg') }); |
|
let block = new THREE.Mesh(blockGeometry, blockMaterial); |
|
block.position.set(0, 5, 0); |
|
scene.add(block); |
|
blocks.push(block); |
|
currentBlock = block; |
|
|
|
|
|
let blockShape = new CANNON.Box(new CANNON.Vec3(5, 5, 5)); |
|
let blockBody = new CANNON.Body({ mass: 1 }); |
|
blockBody.addShape(blockShape); |
|
blockBody.position.set(0, 15, 0); |
|
world.addBody(blockBody); |
|
block.userData.physicsBody = blockBody; |
|
} |
|
|
|
function placeBlock() { |
|
if (currentBlock) { |
|
let position = currentBlock.position.clone(); |
|
let body = currentBlock.userData.physicsBody; |
|
body.position.copy(position); |
|
body.velocity.set(0, 0, 0); |
|
body.angularVelocity.set(0, 0, 0); |
|
stack.push(currentBlock); |
|
currentBlock = null; |
|
} |
|
} |
|
|
|
function steerBlock(direction) { |
|
if (currentBlock) { |
|
let position = currentBlock.position.clone(); |
|
position.x += direction * 10; |
|
currentBlock.position.copy(position); |
|
} |
|
} |
|
|
|
function onKeyDown(event) { |
|
if (event.keyCode === 65) { |
|
steerBlock(-1); |
|
} else if (event.keyCode === 68) { |
|
steerBlock(1); |
|
} else if (event.keyCode === 32) { |
|
placeBlock(); |
|
let stability = checkStability(); |
|
if (stability) { |
|
score += Math.pow(2, stack.length); |
|
document.getElementById('score').innerHTML = 'Score: ' + score; |
|
} else { |
|
alert('Game Over!'); |
|
} |
|
} |
|
} |
|
|
|
function checkStability() { |
|
let positions = []; |
|
for (let i = 0; i < stack.length; i++) { |
|
let block = stack[i]; |
|
let position = block.userData.physicsBody.position; |
|
positions.push(position.clone()); |
|
} |
|
for (let i = 0; i < stack.length - 1; i++) { |
|
let block1 = stack[i]; |
|
let position1 = positions[i]; |
|
for (let j = i + 1; j < stack.length; j++) { |
|
let block2 = stack[j]; |
|
let position2 = positions[j]; |
|
let distance = position1.distanceTo(position2); |
|
if (distance < 10) { |
|
return false; |
|
} |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
function resetGame() { |
|
|
|
while (blocks.length > 0) { |
|
let block = blocks.pop(); |
|
scene.remove(block); |
|
world.remove(block.userData.physicsBody); |
|
} |
|
|
|
|
|
stack = []; |
|
score = 0; |
|
document.getElementById('score').innerHTML = 'Score: ' + score; |
|
|
|
|
|
createBlock(); |
|
} |
|
|
|
resetGame(); |
|
</script> |
|
<div id="score" style="position: absolute; top: 10px; left: 10px; font-size: 24px; font-weight: bold; color: white;"></div> |
|
<button onclick="resetGame()" style="position: absolute; top: 10px; right: 10px; font-size: 24px;">Restart</button> |
|
|
|
</body> |
|
</html> |