test / index.html
xiangfeng's picture
Add 1 files
7f530d9 verified
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D太空射击游戏</title>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/controls/PointerLockControls.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
background-color: #000;
font-family: Arial, sans-serif;
}
canvas {
display: block;
}
#menu {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
text-align: center;
z-index: 100;
}
#startButton {
background: linear-gradient(to bottom, #4a6bff, #0026ff);
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 10px 2px;
cursor: pointer;
border-radius: 5px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
transition: all 0.3s;
}
#startButton:hover {
background: linear-gradient(to bottom, #3a5bef, #0015ee);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.4);
}
#hud {
position: absolute;
top: 0;
left: 0;
width: 100%;
padding: 20px;
color: white;
font-size: 18px;
z-index: 10;
pointer-events: none;
}
#crosshair {
position: absolute;
top: 50%;
left: 50%;
width: 20px;
height: 20px;
transform: translate(-50%, -50%);
pointer-events: none;
opacity: 0.8;
}
#gameOver {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
display: none;
flex-direction: column;
justify-content: center;
align-items: center;
color: white;
font-size: 36px;
z-index: 100;
}
</style>
</head>
<body>
<div id="menu">
<h1>太空射击战争</h1>
<p>准备好驾驶你的战机与外星人战斗了吗?</p>
<button id="startButton">开始战斗</button>
</div>
<div id="hud">
分数: <span id="score">0</span> |
生命: <span id="health">100</span>% |
敌人剩余: <span id="enemies">0</span>
</div>
<div id="crosshair">
<svg width="20" height="20" viewBox="0 0 20 20">
<circle cx="10" cy="10" r="5" stroke="white" stroke-width="2" fill="none"/>
<line x1="10" y1="0" x2="10" y2="6" stroke="white" stroke-width="2"/>
<line x1="10" y1="14" x2="10" y2="20" stroke="white" stroke-width="2"/>
<line x1="0" y1="10" x2="6" y2="10" stroke="white" stroke-width="2"/>
<line x1="14" y1="10" x2="20" y2="10" stroke="white" stroke-width="2"/>
</svg>
</div>
<div id="gameOver">
<h1>游戏结束</h1>
<p>你的最终分数: <span id="finalScore">0</span></p>
<button id="restartButton">再来一次</button>
</div>
<script>
// 游戏状态
const gameState = {
score: 0,
health: 100,
enemies: 0,
isGameOver: false,
isGameStarted: false,
enemySpawnRate: 1000, // 敌人生成间隔(毫秒)
lastSpawnTime: 0,
lastBulletTime: 0,
bulletCooldown: 200,
bullets: [],
enemies: [],
explosions: []
};
// Three.js变量
let scene, camera, renderer, controls;
let spaceship, stars = [], enemySpawnInterval;
const clock = new THREE.Clock();
const movementSpeed = 20;
const rotationSpeed = 0.002;
// 初始化Three.js场景
function init() {
// 创建场景
scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0x000033, 0.001);
// 创建相机
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
// 创建渲染器
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
// 添加光源
addLights();
// 创建星空背景
createStarfield();
// 创建玩家飞船
createSpaceship();
// 添加事件监听器
window.addEventListener('resize', onWindowResize);
document.getElementById('startButton').addEventListener('click', startGame);
document.getElementById('restartButton').addEventListener('click', restartGame);
document.addEventListener('keydown', onKeyDown);
document.addEventListener('keyup', onKeyUp);
document.addEventListener('click', fireBullet);
}
// 添加光源
function addLights() {
const ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(1, 1, 1);
directionalLight.castShadow = true;
directionalLight.shadow.mapSize.width = 2048;
directionalLight.shadow.mapSize.height = 2048;
scene.add(directionalLight);
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 1);
hemiLight.position.set(0, 20, 0);
scene.add(hemiLight);
}
// 创建星空背景
function createStarfield() {
for (let i = 0; i < 1000; i++) {
const geometry = new THREE.SphereGeometry(0.1, 8, 8);
const material = new THREE.MeshBasicMaterial({
color: Math.random() > 0.5 ? 0xffffff : 0xaaaaaa,
transparent: true,
opacity: Math.random()
});
const star = new THREE.Mesh(geometry, material);
star.position.x = Math.random() * 2000 - 1000;
star.position.y = Math.random() * 2000 - 1000;
star.position.z = Math.random() * 2000 - 1000;
stars.push(star);
scene.add(star);
}
}
// 创建玩家飞船
function createSpaceship() {
const group = new THREE.Group();
// 机身
const bodyGeometry = new THREE.ConeGeometry(1, 3, 8);
const bodyMaterial = new THREE.MeshPhongMaterial({
color: 0x3366ff,
flatShading: true,
emissive: 0x0033cc,
emissiveIntensity: 0.5
});
const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
body.rotation.x = Math.PI / 2;
group.add(body);
// 机翼
const wingGeometry = new THREE.BoxGeometry(2, 0.2, 1);
const wingMaterial = new THREE.MeshPhongMaterial({
color: 0x0044ff,
flatShading: true
});
const leftWing = new THREE.Mesh(wingGeometry, wingMaterial);
leftWing.position.set(-1, 0, 0);
group.add(leftWing);
const rightWing = new THREE.Mesh(wingGeometry, wingMaterial);
rightWing.position.set(1, 0, 0);
group.add(rightWing);
// 尾翼
const tailWingGeometry = new THREE.BoxGeometry(0.2, 1, 0.8);
const tailWing = new THREE.Mesh(tailWingGeometry, wingMaterial);
tailWing.position.set(0, 0, -1.5);
group.add(tailWing);
// 引擎光效
const engineLightGeometry = new THREE.SphereGeometry(0.5, 8, 8);
const engineLightMaterial = new THREE.MeshBasicMaterial({
color: 0xffff00,
transparent: true,
opacity: 0.8
});
const engineLight = new THREE.Mesh(engineLightGeometry, engineLightMaterial);
engineLight.position.set(0, 0, 1.5);
group.add(engineLight);
group.position.z = -10;
scene.add(group);
spaceship = group;
}
// 创建敌人飞船
function createEnemy() {
const group = new THREE.Group();
// 机身
const bodyGeometry = new THREE.OctahedronGeometry(1);
const bodyMaterial = new THREE.MeshPhongMaterial({
color: 0xff0000,
flatShading: true,
emissive: 0xcc0000,
emissiveIntensity: 0.2
});
const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
group.add(body);
// 运动参数
group.userData = {
speed: Math.random() * 2 + 1,
health: 30,
lastShot: 0,
shotCooldown: 2000 + Math.random() * 2000
};
// 随机位置(远离玩家)
const distance = 50 + Math.random() * 50;
const angle = Math.random() * Math.PI * 2;
group.position.x = Math.cos(angle) * distance;
group.position.y = Math.sin(angle) * distance;
group.position.z = -100 - Math.random() * 50;
// 朝向玩家
group.lookAt(spaceship.position);
scene.add(group);
gameState.enemies.push(group);
gameState.enemies++;
updateHUD();
return group;
}
// 创建子弹
function createBullet(position, direction, isPlayerBullet = true) {
const geometry = new THREE.SphereGeometry(0.1, 16, 16);
const material = new THREE.MeshBasicMaterial({
color: isPlayerBullet ? 0x00ffff : 0xff6600,
emissive: isPlayerBullet ? 0x00aaff : 0xff3300,
emissiveIntensity: 0.8
});
const bullet = new THREE.Mesh(geometry, material);
bullet.position.copy(position);
bullet.userData = {
direction: direction.clone().normalize(),
speed: isPlayerBullet ? 20 : 10,
damage: isPlayerBullet ? 10 : 5,
isPlayerBullet
};
scene.add(bullet);
gameState.bullets.push(bullet);
return bullet;
}
// 创建爆炸效果
function createExplosion(position) {
const particles = 50;
const geometry = new THREE.BufferGeometry();
const positions = new Float32Array(particles * 3);
const sizes = new Float32Array(particles);
for (let i = 0; i < particles; i++) {
positions[i * 3] = (Math.random() - 0.5) * 4;
positions[i * 3 + 1] = (Math.random() - 0.5) * 4;
positions[i * 3 + 2] = (Math.random() - 0.5) * 4;
sizes[i] = Math.random() * 0.5 + 0.1;
}
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1));
const material = new THREE.PointsMaterial({
color: new THREE.Color(Math.random() > 0.5 ? 0xff6600 : 0xff0000),
size: 0.1,
transparent: true,
opacity: 1,
sizeAttenuation: true
});
const particleSystem = new THREE.Points(geometry, material);
particleSystem.position.copy(position);
particleSystem.userData = {
life: 1.0,
decay: 0.05
};
scene.add(particleSystem);
gameState.explosions.push(particleSystem);
}
// 开火
function fireBullet() {
if (!gameState.isGameStarted || gameState.isGameOver) return;
const now = Date.now();
if (now - gameState.lastBulletTime < gameState.bulletCooldown) return;
gameState.lastBulletTime = now;
// 从飞船前方发射
const bulletPosition = new THREE.Vector3();
spaceship.getWorldPosition(bulletPosition);
// 稍微前移一点
bulletPosition.z -= 1;
// 向相机正前方发射
const direction = new THREE.Vector3(0, 0, -1);
direction.applyQuaternion(camera.quaternion);
createBullet(bulletPosition, direction);
// 播放射击音效
playSound('shoot', 0.1);
}
// 敌人开火
function enemyFire(enemy) {
const now = Date.now();
if (now - enemy.userData.lastShot < enemy.userData.shotCooldown) return;
enemy.userData.lastShot = now;
const bulletPosition = new THREE.Vector3();
enemy.getWorldPosition(bulletPosition);
// 朝向玩家
const direction = new THREE.Vector3();
spaceship.getWorldPosition(direction);
direction.sub(bulletPosition).normalize();
createBullet(bulletPosition, direction, false);
}
// 开始游戏
function startGame() {
document.getElementById('menu').style.display = 'none';
gameState.isGameStarted = true;
// 初始化指针锁定控制
controls = new THREE.PointerLockControls(camera, document.body);
scene.add(controls.getObject());
// 请求指针锁定
document.body.requestPointerLock = document.body.requestPointerLock ||
document.body.mozRequestPointerLock ||
document.body.webkitRequestPointerLock;
document.body.requestPointerLock();
// 定期生成敌人
enemySpawnInterval = setInterval(() => {
if (!gameState.isGameOver) {
createEnemy();
}
}, gameState.enemySpawnRate);
// 开始游戏循环
animate();
}
// 游戏结束
function gameOver() {
gameState.isGameOver = true;
clearInterval(enemySpawnInterval);
document.exitPointerLock();
document.getElementById('finalScore').textContent = gameState.score;
document.getElementById('gameOver').style.display = 'flex';
}
// 重新开始游戏
function restartGame() {
// 重置游戏状态
gameState.score = 0;
gameState.health = 100;
gameState.enemies = 0;
gameState.isGameOver = false;
// 清除所有敌人和子弹
gameState.enemies.forEach(enemy => scene.remove(enemy));
gameState.bullets.forEach(bullet => scene.remove(bullet));
gameState.explosions.forEach(explosion => scene.remove(explosion));
gameState.enemies = [];
gameState.bullets = [];
gameState.explosions = [];
// 重置飞船位置
spaceship.position.set(0, 0, -10);
spaceship.rotation.set(0, 0, 0);
// 隐藏游戏结束界面
document.getElementById('gameOver').style.display = 'none';
// 更新HUD
updateHUD();
// 重新开始游戏
startGame();
}
// 更新HUD
function updateHUD() {
document.getElementById('score').textContent = gameState.score;
document.getElementById('health').textContent = gameState.health;
document.getElementById('enemies').textContent = gameState.enemies;
}
// 检查碰撞
function checkCollisions() {
// 玩家子弹与敌人碰撞
for (let i = gameState.bullets.length - 1; i >= 0; i--) {
const bullet = gameState.bullets[i];
if (bullet.userData.isPlayerBullet) {
for (let j = gameState.enemies.length - 1; j >= 0; j--) {
const enemy = gameState.enemies[j];
if (bullet.position.distanceTo(enemy.position) < 1.5) {
// 击中敌人
enemy.userData.health -= bullet.userData.damage;
// 创建爆炸效果
createExplosion(bullet.position);
// 从场景中移除子弹
scene.remove(bullet);
gameState.bullets.splice(i, 1);
// 检查敌人是否被摧毁
if (enemy.userData.health <= 0) {
gameState.score += 10;
gameState.enemies--;
createExplosion(enemy.position);
scene.remove(enemy);
gameState.enemies.splice(j, 1);
// 播放爆炸音效
playSound('explosion', 0.3);
} else {
// 播放击中音效
playSound('hit', 0.2);
}
updateHUD();
break;
}
}
} else {
// 敌人子弹与玩家碰撞
if (bullet.position.distanceTo(spaceship.position) < 2) {
// 击中玩家
gameState.health -= bullet.userData.damage;
createExplosion(bullet.position);
scene.remove(bullet);
gameState.bullets.splice(i, 1);
updateHUD();
// 播放击中音效
playSound('hit', 0.3);
// 检查游戏是否结束
if (gameState.health <= 0) {
gameState.health = 0;
gameOver();
}
}
}
}
// 敌人与玩家碰撞
gameState.enemies.forEach(enemy => {
if (enemy.position.distanceTo(spaceship.position) < 3) {
gameState.health -= 2;
updateHUD();
// 播放撞击音效
playSound('collision', 0.5);
if (gameState.health <= 0) {
gameState.health = 0;
gameOver();
}
}
});
}
// 播放音效(简单实现)
function playSound(type, volume) {
if (!window.AudioContext) return;
const ctx = new (window.AudioContext || window.webkitAudioContext)();
const oscillator = ctx.createOscillator();
const gainNode = ctx.createGain();
oscillator.connect(gainNode);
gainNode.connect(ctx.destination);
gainNode.gain.value = volume;
// 根据类型设置不同的音效
switch (type) {
case 'shoot':
oscillator.type = 'square';
oscillator.frequency.value = 220;
oscillator.frequency.exponentialRampToValueAtTime(100, ctx.currentTime + 0.1);
break;
case 'hit':
oscillator.type = 'sine';
oscillator.frequency.value = 440;
oscillator.frequency.exponentialRampToValueAtTime(200, ctx.currentTime + 0.1);
break;
case 'explosion':
oscillator.type = 'sawtooth';
oscillator.frequency.value = 60;
oscillator.frequency.exponentialRampToValueAtTime(20, ctx.currentTime + 0.5);
break;
case 'collision':
oscillator.type = 'sine';
oscillator.frequency.value = 110;
oscillator.frequency.exponentialRampToValueAtTime(55, ctx.currentTime + 0.5);
break;
}
oscillator.start();
oscillator.stop(ctx.currentTime + 0.5);
}
// 键盘控制
const keys = {};
function onKeyDown(event) {
keys[event.code] = true;
}
function onKeyUp(event) {
keys[event.code] = false;
}
// 处理输入
function processInput() {
const delta = clock.getDelta();
// 处理移动
if (keys['KeyW']) {
camera.position.z -= movementSpeed * delta;
spaceship.position.z -= movementSpeed * delta;
}
if (keys['KeyS']) {
camera.position.z += movementSpeed * delta;
spaceship.position.z += movementSpeed * delta;
}
if (keys['KeyA']) {
camera.position.x -= movementSpeed * delta;
spaceship.position.x -= movementSpeed * delta;
}
if (keys['KeyD']) {
camera.position.x += movementSpeed * delta;
spaceship.position.x += movementSpeed * delta;
}
if (keys['Space']) {
camera.position.y += movementSpeed * delta;
spaceship.position.y += movementSpeed * delta;
}
if (keys['ShiftLeft']) {
camera.position.y -= movementSpeed * delta;
spaceship.position.y -= movementSpeed * delta;
}
// 限制玩家移动范围
const maxDistance = 100;
spaceship.position.x = Math.max(-maxDistance, Math.min(maxDistance, spaceship.position.x));
spaceship.position.y = Math.max(5, Math.min(maxDistance, spaceship.position.y));
spaceship.position.z = Math.max(-maxDistance-10, Math.min(20, spaceship.position.z));
camera.position.copy(spaceship.position);
camera.position.z += 5;
}
// 更新游戏
function update(delta) {
// 更新子弹
for (let i = gameState.bullets.length - 1; i >= 0; i--) {
const bullet = gameState.bullets[i];
bullet.position.add(bullet.userData.direction.multiplyScalar(bullet.userData.speed * delta));
// 移除超出范围的子弹
if (bullet.position.length() > 500) {
scene.remove(bullet);
gameState.bullets.splice(i, 1);
}
}
// 更新敌人
gameState.enemies.forEach(enemy => {
// 移动敌人
enemy.lookAt(spaceship.position);
const direction = new THREE.Vector3();
spaceship.getWorldPosition(direction);
direction.sub(enemy.position).normalize();
enemy.position.add(direction.multiplyScalar(enemy.userData.speed * delta));
// 敌人开火
if (Math.random() < 0.01) {
enemyFire(enemy);
}
});
// 更新爆炸效果
for (let i = gameState.explosions.length - 1; i >= 0; i--) {
const explosion = gameState.explosions[i];
explosion.userData.life -= explosion.userData.decay;
if (explosion.userData.life <= 0) {
scene.remove(explosion);
gameState.explosions.splice(i, 1);
} else {
explosion.material.opacity = explosion.userData.life;
}
}
// 检查碰撞
checkCollisions();
}
// 窗口大小调整
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
// 动画循环
function animate() {
if (gameState.isGameOver) return;
requestAnimationFrame(animate);
const delta = clock.getDelta();
if (gameState.isGameStarted && !gameState.isGameOver) {
processInput();
update(delta);
controls.update(delta);
}
renderer.render(scene, camera);
}
// 初始化游戏
init();
</script>
</body>
</html>