Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>太空射击 - 3D 射击游戏</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/three@0.128.0/examples/js/controls/OrbitControls.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/GLTFLoader.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/effects/OutlineEffect.js"></script> | |
| <style> | |
| body { | |
| margin: 0; | |
| overflow: hidden; | |
| font-family: 'Arial', sans-serif; | |
| } | |
| #game-container { | |
| position: relative; | |
| width: 100vw; | |
| height: 100vh; | |
| } | |
| #ui-overlay { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| pointer-events: none; | |
| color: white; | |
| font-family: 'Orbitron', sans-serif; | |
| } | |
| #health-bar { | |
| position: absolute; | |
| top: 20px; | |
| left: 20px; | |
| width: 200px; | |
| height: 20px; | |
| background-color: rgba(255, 0, 0, 0.3); | |
| border: 2px solid white; | |
| border-radius: 5px; | |
| overflow: hidden; | |
| } | |
| #health-fill { | |
| height: 100%; | |
| width: 100%; | |
| background-color: #ff3366; | |
| transition: width 0.3s; | |
| } | |
| #score { | |
| position: absolute; | |
| top: 20px; | |
| right: 20px; | |
| font-size: 24px; | |
| color: white; | |
| text-shadow: 0 0 10px #00ffff; | |
| } | |
| #level { | |
| position: absolute; | |
| top: 60px; | |
| right: 20px; | |
| font-size: 18px; | |
| color: white; | |
| text-shadow: 0 0 5px #00ffff; | |
| } | |
| #ammo { | |
| position: absolute; | |
| bottom: 20px; | |
| left: 20px; | |
| font-size: 18px; | |
| color: white; | |
| text-shadow: 0 0 5px #00ffff; | |
| } | |
| #game-over, #level-complete, #start-screen { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| align-items: center; | |
| background-color: rgba(0, 0, 0, 0.8); | |
| color: white; | |
| font-size: 36px; | |
| pointer-events: auto; | |
| } | |
| .button { | |
| margin-top: 20px; | |
| padding: 10px 30px; | |
| background: linear-gradient(45deg, #ff3366, #00ffff); | |
| border: none; | |
| border-radius: 5px; | |
| color: white; | |
| font-size: 18px; | |
| cursor: pointer; | |
| pointer-events: auto; | |
| transition: all 0.3s; | |
| } | |
| .button:hover { | |
| transform: scale(1.05); | |
| box-shadow: 0 0 15px #00ffff; | |
| } | |
| #enemy-count { | |
| position: absolute; | |
| top: 60px; | |
| left: 20px; | |
| font-size: 18px; | |
| color: white; | |
| text-shadow: 0 0 5px #00ffff; | |
| } | |
| #boss-health { | |
| position: absolute; | |
| top: 100px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| width: 300px; | |
| height: 20px; | |
| background-color: rgba(255, 0, 0, 0.3); | |
| border: 2px solid white; | |
| border-radius: 5px; | |
| overflow: hidden; | |
| display: none; | |
| } | |
| #boss-health-fill { | |
| height: 100%; | |
| width: 100%; | |
| background-color: #ff3366; | |
| transition: width 0.3s; | |
| } | |
| #power-ups { | |
| position: absolute; | |
| bottom: 60px; | |
| left: 20px; | |
| display: flex; | |
| gap: 10px; | |
| } | |
| .power-up-icon { | |
| width: 40px; | |
| height: 40px; | |
| border-radius: 50%; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| font-size: 20px; | |
| background-color: rgba(255, 255, 255, 0.2); | |
| border: 2px solid white; | |
| } | |
| #crosshair { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| width: 30px; | |
| height: 30px; | |
| pointer-events: none; | |
| } | |
| #crosshair::before, #crosshair::after { | |
| content: ''; | |
| position: absolute; | |
| background-color: rgba(255, 255, 255, 0.8); | |
| } | |
| #crosshair::before { | |
| width: 30px; | |
| height: 2px; | |
| left: 0; | |
| top: 14px; | |
| } | |
| #crosshair::after { | |
| width: 2px; | |
| height: 30px; | |
| left: 14px; | |
| top: 0; | |
| } | |
| #crosshair.active { | |
| animation: pulse 0.5s infinite alternate; | |
| } | |
| @keyframes pulse { | |
| from { transform: translate(-50%, -50%) scale(1); } | |
| to { transform: translate(-50%, -50%) scale(1.2); } | |
| } | |
| #radar { | |
| position: absolute; | |
| bottom: 20px; | |
| right: 20px; | |
| width: 150px; | |
| height: 150px; | |
| border-radius: 50%; | |
| background-color: rgba(0, 0, 0, 0.5); | |
| border: 2px solid #00ffff; | |
| overflow: hidden; | |
| } | |
| #radar-center { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| width: 2px; | |
| height: 2px; | |
| border-radius: 50%; | |
| background-color: #00ffff; | |
| } | |
| .radar-sweep { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| border-radius: 50%; | |
| clip-path: polygon(50% 50%, 50% 0, 50% 0); | |
| background-color: rgba(0, 255, 255, 0.2); | |
| animation: radarSweep 3s linear infinite; | |
| } | |
| @keyframes radarSweep { | |
| from { transform: rotate(0deg); } | |
| to { transform: rotate(360deg); } | |
| } | |
| .radar-blip { | |
| position: absolute; | |
| width: 6px; | |
| height: 6px; | |
| border-radius: 50%; | |
| background-color: #ff3366; | |
| transform: translate(-50%, -50%); | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="game-container"> | |
| <div id="ui-overlay"> | |
| <div id="health-bar"> | |
| <div id="health-fill"></div> | |
| </div> | |
| <div id="score">分数: 0</div> | |
| <div id="level">关卡: 1</div> | |
| <div id="enemy-count">敌人: 0</div> | |
| <div id="boss-health"> | |
| <div id="boss-health-fill"></div> | |
| </div> | |
| <div id="ammo">弹药: ∞</div> | |
| <div id="power-ups"></div> | |
| <div id="crosshair"></div> | |
| <div id="radar"> | |
| <div id="radar-center"></div> | |
| <div class="radar-sweep"></div> | |
| </div> | |
| <div id="start-screen"> | |
| <h1 class="text-5xl font-bold mb-8 text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-purple-600">太空射击</h1> | |
| <p class="text-xl mb-8">保卫银河系免受外星入侵</p> | |
| <button id="start-button" class="button">开始游戏</button> | |
| <div class="mt-8 text-sm"> | |
| <p>控制方式: WASD移动, 鼠标瞄准射击</p> | |
| <p>空格键: 特殊武器</p> | |
| </div> | |
| </div> | |
| <div id="game-over" style="display: none;"> | |
| <h1 class="text-5xl font-bold mb-8 text-transparent bg-clip-text bg-gradient-to-r from-red-400 to-purple-600">游戏结束</h1> | |
| <p class="text-xl mb-4">最终分数: <span id="final-score">0</span></p> | |
| <p class="text-xl mb-8">最高关卡: <span id="final-level">1</span></p> | |
| <button id="restart-button" class="button">重新开始</button> | |
| </div> | |
| <div id="level-complete" style="display: none;"> | |
| <h1 class="text-5xl font-bold mb-8 text-transparent bg-clip-text bg-gradient-to-r from-green-400 to-blue-600">关卡完成!</h1> | |
| <p class="text-xl mb-4">分数: <span id="level-score">0</span></p> | |
| <p class="text-xl mb-8">下一关卡: <span id="next-level">2</span></p> | |
| <button id="next-level-button" class="button">继续</button> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // 游戏常量 | |
| const GAME = { | |
| WIDTH: window.innerWidth, | |
| HEIGHT: window.innerHeight, | |
| ASPECT: window.innerWidth / window.innerHeight, | |
| NEAR: 0.1, | |
| FAR: 10000, | |
| FOV: 75, | |
| PLAYER_SPEED: 0.3, | |
| BULLET_SPEED: 1.5, | |
| ENEMY_SPEED: 0.1, | |
| BOSS_SPEED: 0.05, | |
| POWERUP_DURATION: 10000, // 10秒 | |
| LEVELS: [ | |
| { enemies: 10, enemyHealth: 1, spawnRate: 1000, boss: false }, | |
| { enemies: 15, enemyHealth: 2, spawnRate: 800, boss: false }, | |
| { enemies: 20, enemyHealth: 3, spawnRate: 600, boss: false }, | |
| { enemies: 25, enemyHealth: 4, spawnRate: 500, boss: true }, | |
| { enemies: 30, enemyHealth: 5, spawnRate: 400, boss: false }, | |
| { enemies: 35, enemyHealth: 6, spawnRate: 300, boss: false }, | |
| { enemies: 40, enemyHealth: 7, spawnRate: 200, boss: true }, | |
| { enemies: 50, enemyHealth: 8, spawnRate: 100, boss: true } | |
| ] | |
| }; | |
| // 游戏状态 | |
| let gameState = { | |
| score: 0, | |
| level: 1, | |
| health: 100, | |
| maxHealth: 100, | |
| ammo: Infinity, | |
| gameOver: false, | |
| levelComplete: false, | |
| enemies: [], | |
| bullets: [], | |
| enemyBullets: [], | |
| powerUps: [], | |
| activePowerUps: {}, | |
| boss: null, | |
| bossActive: false, | |
| enemiesSpawned: 0, | |
| enemiesDestroyed: 0, | |
| lastShot: 0, | |
| shotDelay: 200, | |
| lastEnemyShot: 0, | |
| enemyShotDelay: 1000, | |
| lastPowerUpSpawn: 0, | |
| powerUpSpawnDelay: 15000, | |
| specialWeaponReady: true, | |
| specialWeaponCooldown: 10000, | |
| lastSpecialWeaponUse: 0 | |
| }; | |
| // Three.js 变量 | |
| let scene, camera, renderer, controls, effect; | |
| let player, playerGroup; | |
| let clock = new THREE.Clock(); | |
| let raycaster = new THREE.Raycaster(); | |
| let mouse = new THREE.Vector2(); | |
| let isMouseDown = false; | |
| let keys = {}; | |
| let radarBlips = []; | |
| // 初始化游戏 | |
| function init() { | |
| // 创建场景 | |
| scene = new THREE.Scene(); | |
| scene.background = new THREE.Color(0x000000); | |
| scene.fog = new THREE.FogExp2(0x000000, 0.0005); | |
| // 创建相机 | |
| camera = new THREE.PerspectiveCamera(GAME.FOV, GAME.ASPECT, GAME.NEAR, GAME.FAR); | |
| camera.position.set(0, 5, 15); | |
| camera.lookAt(0, 0, 0); | |
| // 创建渲染器 | |
| renderer = new THREE.WebGLRenderer({ antialias: true }); | |
| renderer.setSize(GAME.WIDTH, GAME.HEIGHT); | |
| renderer.shadowMap.enabled = true; | |
| document.getElementById('game-container').appendChild(renderer.domElement); | |
| // 添加轮廓效果 | |
| effect = new THREE.OutlineEffect(renderer); | |
| // 添加灯光 | |
| addLights(); | |
| // 添加星空背景 | |
| createStarfield(); | |
| // 创建玩家飞船 | |
| createPlayer(); | |
| // 添加事件监听器 | |
| setupEventListeners(); | |
| // 显示开始屏幕 | |
| document.getElementById('start-screen').style.display = 'flex'; | |
| // 开始游戏循环 | |
| animate(); | |
| } | |
| // 添加灯光 | |
| 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 = 1024; | |
| directionalLight.shadow.mapSize.height = 1024; | |
| scene.add(directionalLight); | |
| // 点光源 | |
| const pointLight = new THREE.PointLight(0x00ffff, 2, 50); | |
| pointLight.position.set(0, 5, 0); | |
| scene.add(pointLight); | |
| // 聚光灯 | |
| const spotLight = new THREE.SpotLight(0xffffff, 1); | |
| spotLight.position.set(0, 10, 0); | |
| spotLight.castShadow = true; | |
| spotLight.shadow.mapSize.width = 1024; | |
| spotLight.shadow.mapSize.height = 1024; | |
| scene.add(spotLight); | |
| } | |
| // 创建星空背景 | |
| function createStarfield() { | |
| const starGeometry = new THREE.BufferGeometry(); | |
| const starMaterial = new THREE.PointsMaterial({ | |
| color: 0xffffff, | |
| size: 0.1, | |
| transparent: true, | |
| opacity: 0.8 | |
| }); | |
| const stars = []; | |
| for (let i = 0; i < 5000; i++) { | |
| const x = (Math.random() - 0.5) * 2000; | |
| const y = (Math.random() - 0.5) * 2000; | |
| const z = (Math.random() - 0.5) * 2000; | |
| stars.push(x, y, z); | |
| } | |
| starGeometry.setAttribute('position', new THREE.Float32BufferAttribute(stars, 3)); | |
| const starField = new THREE.Points(starGeometry, starMaterial); | |
| scene.add(starField); | |
| } | |
| // 创建玩家飞船 | |
| function createPlayer() { | |
| playerGroup = new THREE.Group(); | |
| // 创建飞船主体 | |
| const geometry = new THREE.ConeGeometry(0.5, 1.5, 4); | |
| const material = new THREE.MeshPhongMaterial({ | |
| color: 0x3366ff, | |
| emissive: 0x0044cc, | |
| specular: 0xffffff, | |
| shininess: 30, | |
| flatShading: true | |
| }); | |
| player = new THREE.Mesh(geometry, material); | |
| player.rotation.x = Math.PI / 2; | |
| player.position.y = 0.5; | |
| player.castShadow = true; | |
| // 添加引擎火焰 | |
| const flameGeometry = new THREE.ConeGeometry(0.3, 1, 4); | |
| const flameMaterial = new THREE.MeshBasicMaterial({ | |
| color: 0xff6600, | |
| transparent: true, | |
| opacity: 0.8 | |
| }); | |
| const flame = new THREE.Mesh(flameGeometry, flameMaterial); | |
| flame.rotation.x = Math.PI / 2; | |
| flame.position.z = -1; | |
| player.add(flame); | |
| // 添加机翼 | |
| const wingGeometry = new THREE.BoxGeometry(1, 0.1, 0.5); | |
| const wingMaterial = new THREE.MeshPhongMaterial({ | |
| color: 0x3366ff, | |
| emissive: 0x0044cc, | |
| specular: 0xffffff, | |
| shininess: 30, | |
| flatShading: true | |
| }); | |
| const leftWing = new THREE.Mesh(wingGeometry, wingMaterial); | |
| leftWing.position.set(-0.8, 0, 0); | |
| player.add(leftWing); | |
| const rightWing = new THREE.Mesh(wingGeometry, wingMaterial); | |
| rightWing.position.set(0.8, 0, 0); | |
| player.add(rightWing); | |
| playerGroup.add(player); | |
| playerGroup.position.set(0, 0, -10); | |
| scene.add(playerGroup); | |
| } | |
| // 创建敌人 | |
| function createEnemy(x, y, z, health = 1) { | |
| const enemyGroup = new THREE.Group(); | |
| // 创建敌人主体 | |
| const geometry = new THREE.OctahedronGeometry(0.6); | |
| const material = new THREE.MeshPhongMaterial({ | |
| color: health > 3 ? 0xff0000 : (health > 1 ? 0xff9900 : 0x00ff00), | |
| emissive: health > 3 ? 0x990000 : (health > 1 ? 0x994400 : 0x009900), | |
| specular: 0xffffff, | |
| shininess: 30, | |
| flatShading: true | |
| }); | |
| const enemy = new THREE.Mesh(geometry, material); | |
| enemy.castShadow = true; | |
| enemy.userData.health = health; | |
| enemy.userData.maxHealth = health; | |
| // 添加细节 | |
| const eyeGeometry = new THREE.SphereGeometry(0.1); | |
| const eyeMaterial = new THREE.MeshBasicMaterial({ color: 0x000000 }); | |
| const leftEye = new THREE.Mesh(eyeGeometry, eyeMaterial); | |
| leftEye.position.set(-0.2, 0.2, 0.5); | |
| enemy.add(leftEye); | |
| const rightEye = new THREE.Mesh(eyeGeometry, eyeMaterial); | |
| rightEye.position.set(0.2, 0.2, 0.5); | |
| enemy.add(rightEye); | |
| enemyGroup.add(enemy); | |
| enemyGroup.position.set(x, y, z); | |
| enemyGroup.userData.speed = GAME.ENEMY_SPEED * (1 + Math.random() * 0.5); | |
| enemyGroup.userData.isBoss = false; | |
| scene.add(enemyGroup); | |
| gameState.enemies.push(enemyGroup); | |
| return enemyGroup; | |
| } | |
| // 创建Boss敌人 | |
| function createBoss() { | |
| const bossGroup = new THREE.Group(); | |
| // 创建Boss主体 | |
| const geometry = new THREE.DodecahedronGeometry(2); | |
| const material = new THREE.MeshPhongMaterial({ | |
| color: 0xff0000, | |
| emissive: 0x990000, | |
| specular: 0xffffff, | |
| shininess: 30, | |
| flatShading: true | |
| }); | |
| const boss = new THREE.Mesh(geometry, material); | |
| boss.castShadow = true; | |
| boss.userData.health = 20; | |
| boss.userData.maxHealth = 20; | |
| // 添加细节 | |
| const eyeGeometry = new THREE.SphereGeometry(0.3); | |
| const eyeMaterial = new THREE.MeshBasicMaterial({ color: 0x000000 }); | |
| const leftEye = new THREE.Mesh(eyeGeometry, eyeMaterial); | |
| leftEye.position.set(-0.8, 0.5, 1.8); | |
| boss.add(leftEye); | |
| const rightEye = new THREE.Mesh(eyeGeometry, eyeMaterial); | |
| rightEye.position.set(0.8, 0.5, 1.8); | |
| boss.add(rightEye); | |
| // 添加武器 | |
| const weaponGeometry = new THREE.CylinderGeometry(0.2, 0.2, 1, 6); | |
| const weaponMaterial = new THREE.MeshPhongMaterial({ | |
| color: 0x666666, | |
| emissive: 0x333333, | |
| specular: 0xffffff, | |
| shininess: 30, | |
| flatShading: true | |
| }); | |
| const leftWeapon = new THREE.Mesh(weaponGeometry, weaponMaterial); | |
| leftWeapon.position.set(-1.5, 0, 0); | |
| leftWeapon.rotation.z = Math.PI / 2; | |
| boss.add(leftWeapon); | |
| const rightWeapon = new THREE.Mesh(weaponGeometry, weaponMaterial); | |
| rightWeapon.position.set(1.5, 0, 0); | |
| rightWeapon.rotation.z = Math.PI / 2; | |
| boss.add(rightWeapon); | |
| bossGroup.add(boss); | |
| bossGroup.position.set(0, 0, 50); | |
| bossGroup.userData.speed = GAME.BOSS_SPEED; | |
| bossGroup.userData.isBoss = true; | |
| bossGroup.userData.lastShot = 0; | |
| bossGroup.userData.shotDelay = 500; | |
| scene.add(bossGroup); | |
| gameState.enemies.push(bossGroup); | |
| gameState.boss = bossGroup; | |
| gameState.bossActive = true; | |
| // 显示Boss血条 | |
| document.getElementById('boss-health').style.display = 'block'; | |
| updateBossHealth(); | |
| return bossGroup; | |
| } | |
| // 创建子弹 | |
| function createBullet(x, y, z, isEnemy = false) { | |
| const bulletGroup = new THREE.Group(); | |
| const geometry = new THREE.SphereGeometry(0.1); | |
| const material = new THREE.MeshPhongMaterial({ | |
| color: isEnemy ? 0xff0000 : 0x00ffff, | |
| emissive: isEnemy ? 0x990000 : 0x006666, | |
| specular: 0xffffff, | |
| shininess: 30, | |
| flatShading: true | |
| }); | |
| const bullet = new THREE.Mesh(geometry, material); | |
| bullet.castShadow = true; | |
| // 添加光晕效果 | |
| const glowGeometry = new THREE.SphereGeometry(0.2); | |
| const glowMaterial = new THREE.MeshBasicMaterial({ | |
| color: isEnemy ? 0xff0000 : 0x00ffff, | |
| transparent: true, | |
| opacity: 0.5 | |
| }); | |
| const glow = new THREE.Mesh(glowGeometry, glowMaterial); | |
| bullet.add(glow); | |
| bulletGroup.add(bullet); | |
| bulletGroup.position.set(x, y, z); | |
| bulletGroup.userData.isEnemy = isEnemy; | |
| bulletGroup.userData.speed = isEnemy ? GAME.BULLET_SPEED * 0.8 : GAME.BULLET_SPEED; | |
| scene.add(bulletGroup); | |
| if (isEnemy) { | |
| gameState.enemyBullets.push(bulletGroup); | |
| } else { | |
| gameState.bullets.push(bulletGroup); | |
| } | |
| return bulletGroup; | |
| } | |
| // 创建能量道具 | |
| function createPowerUp(x, y, z) { | |
| const powerUpGroup = new THREE.Group(); | |
| const geometry = new THREE.IcosahedronGeometry(0.4); | |
| const material = new THREE.MeshPhongMaterial({ | |
| color: 0xffff00, | |
| emissive: 0x996600, | |
| specular: 0xffffff, | |
| shininess: 30, | |
| flatShading: true, | |
| transparent: true, | |
| opacity: 0.9 | |
| }); | |
| const powerUp = new THREE.Mesh(geometry, material); | |
| powerUp.castShadow = true; | |
| // 随机选择道具类型 | |
| const types = ['health', 'shield', 'speed', 'fireRate', 'special']; | |
| const type = types[Math.floor(Math.random() * types.length)]; | |
| powerUp.userData.type = type; | |
| // 根据类型改变颜色 | |
| switch (type) { | |
| case 'health': | |
| powerUp.material.color.setHex(0x00ff00); | |
| powerUp.material.emissive.setHex(0x009900); | |
| break; | |
| case 'shield': | |
| powerUp.material.color.setHex(0x3366ff); | |
| powerUp.material.emissive.setHex(0x003399); | |
| break; | |
| case 'speed': | |
| powerUp.material.color.setHex(0xff9900); | |
| powerUp.material.emissive.setHex(0x994400); | |
| break; | |
| case 'fireRate': | |
| powerUp.material.color.setHex(0xff00ff); | |
| powerUp.material.emissive.setHex(0x990099); | |
| break; | |
| case 'special': | |
| powerUp.material.color.setHex(0xff0000); | |
| powerUp.material.emissive.setHex(0x990000); | |
| break; | |
| } | |
| powerUpGroup.add(powerUp); | |
| powerUpGroup.position.set(x, y, z); | |
| powerUpGroup.userData.speed = 0.05; | |
| scene.add(powerUpGroup); | |
| gameState.powerUps.push(powerUpGroup); | |
| return powerUpGroup; | |
| } | |
| // 设置事件监听器 | |
| function setupEventListeners() { | |
| // 鼠标移动 | |
| window.addEventListener('mousemove', (event) => { | |
| mouse.x = (event.clientX / GAME.WIDTH) * 2 - 1; | |
| mouse.y = -(event.clientY / GAME.HEIGHT) * 2 + 1; | |
| }); | |
| // 鼠标按下 | |
| window.addEventListener('mousedown', () => { | |
| isMouseDown = true; | |
| document.getElementById('crosshair').classList.add('active'); | |
| }); | |
| // 鼠标释放 | |
| window.addEventListener('mouseup', () => { | |
| isMouseDown = false; | |
| document.getElementById('crosshair').classList.remove('active'); | |
| }); | |
| // 键盘按下 | |
| window.addEventListener('keydown', (event) => { | |
| keys[event.code] = true; | |
| // 空格键发射特殊武器 | |
| if (event.code === 'Space' && gameState.specialWeaponReady) { | |
| fireSpecialWeapon(); | |
| } | |
| }); | |
| // 键盘释放 | |
| window.addEventListener('keyup', (event) => { | |
| keys[event.code] = false; | |
| }); | |
| // 窗口大小调整 | |
| window.addEventListener('resize', () => { | |
| GAME.WIDTH = window.innerWidth; | |
| GAME.HEIGHT = window.innerHeight; | |
| GAME.ASPECT = GAME.WIDTH / GAME.HEIGHT; | |
| camera.aspect = GAME.ASPECT; | |
| camera.updateProjectionMatrix(); | |
| renderer.setSize(GAME.WIDTH, GAME.HEIGHT); | |
| }); | |
| // 开始按钮 | |
| document.getElementById('start-button').addEventListener('click', startGame); | |
| // 重新开始按钮 | |
| document.getElementById('restart-button').addEventListener('click', restartGame); | |
| // 下一关按钮 | |
| document.getElementById('next-level-button').addEventListener('click', nextLevel); | |
| } | |
| // 开始游戏 | |
| function startGame() { | |
| document.getElementById('start-screen').style.display = 'none'; | |
| gameState = { | |
| score: 0, | |
| level: 1, | |
| health: 100, | |
| maxHealth: 100, | |
| ammo: Infinity, | |
| gameOver: false, | |
| levelComplete: false, | |
| enemies: [], | |
| bullets: [], | |
| enemyBullets: [], | |
| powerUps: [], | |
| activePowerUps: {}, | |
| boss: null, | |
| bossActive: false, | |
| enemiesSpawned: 0, | |
| enemiesDestroyed: 0, | |
| lastShot: 0, | |
| shotDelay: 200, | |
| lastEnemyShot: 0, | |
| enemyShotDelay: 1000, | |
| lastPowerUpSpawn: 0, | |
| powerUpSpawnDelay: 15000, | |
| specialWeaponReady: true, | |
| specialWeaponCooldown: 10000, | |
| lastSpecialWeaponUse: 0 | |
| }; | |
| updateUI(); | |
| spawnEnemies(); | |
| } | |
| // 重新开始游戏 | |
| function restartGame() { | |
| document.getElementById('game-over').style.display = 'none'; | |
| // 清除所有游戏对象 | |
| clearGameObjects(); | |
| // 重置玩家位置 | |
| playerGroup.position.set(0, 0, -10); | |
| startGame(); | |
| } | |
| // 下一关 | |
| function nextLevel() { | |
| document.getElementById('level-complete').style.display = 'none'; | |
| // 清除所有游戏对象 | |
| clearGameObjects(); | |
| // 重置玩家位置 | |
| playerGroup.position.set(0, 0, -10); | |
| // 增加关卡 | |
| gameState.level++; | |
| gameState.enemiesSpawned = 0; | |
| gameState.enemiesDestroyed = 0; | |
| gameState.bossActive = false; | |
| gameState.boss = null; | |
| updateUI(); | |
| spawnEnemies(); | |
| } | |
| // 清除所有游戏对象 | |
| function clearGameObjects() { | |
| // 清除敌人 | |
| gameState.enemies.forEach(enemy => { | |
| scene.remove(enemy); | |
| }); | |
| gameState.enemies = []; | |
| // 清除子弹 | |
| gameState.bullets.forEach(bullet => { | |
| scene.remove(bullet); | |
| }); | |
| gameState.bullets = []; | |
| // 清除敌人子弹 | |
| gameState.enemyBullets.forEach(bullet => { | |
| scene.remove(bullet); | |
| }); | |
| gameState.enemyBullets = []; | |
| // 清除能量道具 | |
| gameState.powerUps.forEach(powerUp => { | |
| scene.remove(powerUp); | |
| }); | |
| gameState.powerUps = []; | |
| // 清除雷达标记 | |
| radarBlips.forEach(blip => { | |
| if (blip.parentNode) { | |
| blip.parentNode.removeChild(blip); | |
| } | |
| }); | |
| radarBlips = []; | |
| // 隐藏Boss血条 | |
| document.getElementById('boss-health').style.display = 'none'; | |
| } | |
| // 生成敌人 | |
| function spawnEnemies() { | |
| const currentLevel = Math.min(gameState.level - 1, GAME.LEVELS.length - 1); | |
| const levelData = GAME.LEVELS[currentLevel]; | |
| // 设置敌人生成间隔 | |
| const spawnInterval = setInterval(() => { | |
| if (gameState.enemiesSpawned >= levelData.enemies) { | |
| clearInterval(spawnInterval); | |
| // 如果是Boss关卡且所有小敌人都被消灭,生成Boss | |
| if (levelData.boss && gameState.enemiesDestroyed >= levelData.enemies && !gameState.bossActive) { | |
| createBoss(); | |
| } | |
| return; | |
| } | |
| // 随机生成敌人位置 | |
| const x = (Math.random() - 0.5) * 30; | |
| const y = (Math.random() - 0.5) * 10; | |
| const z = 50 + Math.random() * 50; | |
| createEnemy(x, y, z, levelData.enemyHealth); | |
| gameState.enemiesSpawned++; | |
| updateUI(); | |
| }, levelData.spawnRate); | |
| } | |
| // 发射子弹 | |
| function fireBullet() { | |
| const now = Date.now(); | |
| if (now - gameState.lastShot < gameState.shotDelay) return; | |
| // 计算射击延迟(如果有火力增强道具) | |
| const fireRatePower = gameState.activePowerUps['fireRate']; | |
| const actualShotDelay = fireRatePower ? gameState.shotDelay * 0.5 : gameState.shotDelay; | |
| if (now - gameState.lastShot < actualShotDelay) return; | |
| gameState.lastShot = now; | |
| // 从玩家位置发射子弹 | |
| const bullet = createBullet( | |
| playerGroup.position.x, | |
| playerGroup.position.y, | |
| playerGroup.position.z + 1 | |
| ); | |
| // 设置子弹方向(朝向鼠标指向的位置) | |
| raycaster.setFromCamera(mouse, camera); | |
| const direction = raycaster.ray.direction.clone(); | |
| direction.multiplyScalar(100); | |
| bullet.userData.direction = direction.normalize(); | |
| // 播放射击音效(这里可以添加音效) | |
| } | |
| // 发射特殊武器 | |
| function fireSpecialWeapon() { | |
| const now = Date.now(); | |
| if (!gameState.specialWeaponReady || now - gameState.lastSpecialWeaponUse < gameState.specialWeaponCooldown) return; | |
| gameState.specialWeaponReady = false; | |
| gameState.lastSpecialWeaponUse = now; | |
| // 创建多个子弹形成扇形攻击 | |
| const bulletCount = 10; | |
| for (let i = 0; i < bulletCount; i++) { | |
| setTimeout(() => { | |
| const angle = (i / bulletCount) * Math.PI - Math.PI / 2; | |
| const bullet = createBullet( | |
| playerGroup.position.x, | |
| playerGroup.position.y, | |
| playerGroup.position.z + 1 | |
| ); | |
| // 设置子弹方向(扇形分布) | |
| const direction = new THREE.Vector3( | |
| Math.sin(angle) * 0.5, | |
| 0, | |
| 1 | |
| ).normalize(); | |
| bullet.userData.direction = direction; | |
| bullet.userData.speed = GAME.BULLET_SPEED * 1.5; | |
| // 特殊武器的子弹更大 | |
| bullet.children[0].scale.set(2, 2, 2); | |
| }, i * 50); | |
| } | |
| // 启动特殊武器冷却计时器 | |
| setTimeout(() => { | |
| gameState.specialWeaponReady = true; | |
| }, gameState.specialWeaponCooldown); | |
| } | |
| // 敌人发射子弹 | |
| function enemyFireBullet(enemy) { | |
| const now = Date.now(); | |
| if (now - enemy.userData.lastShot < enemy.userData.shotDelay) return; | |
| enemy.userData.lastShot = now; | |
| // 从敌人位置发射子弹 | |
| const bullet = createBullet( | |
| enemy.position.x, | |
| enemy.position.y, | |
| enemy.position.z - 1, | |
| true | |
| ); | |
| // 设置子弹方向(朝向玩家) | |
| const direction = new THREE.Vector3( | |
| playerGroup.position.x - enemy.position.x, | |
| playerGroup.position.y - enemy.position.y, | |
| playerGroup.position.z - enemy.position.z | |
| ).normalize(); | |
| bullet.userData.direction = direction; | |
| // Boss发射更多的子弹 | |
| if (enemy.userData.isBoss) { | |
| // 创建额外的子弹,形成扇形攻击 | |
| for (let i = 0; i < 3; i++) { | |
| setTimeout(() => { | |
| const angle = (i - 1) * 0.2; | |
| const bullet = createBullet( | |
| enemy.position.x, | |
| enemy.position.y, | |
| enemy.position.z - 1, | |
| true | |
| ); | |
| const dir = direction.clone(); | |
| dir.x += angle; | |
| bullet.userData.direction = dir.normalize(); | |
| }, i * 100); | |
| } | |
| } | |
| } | |
| // 生成能量道具 | |
| function spawnPowerUp() { | |
| const now = Date.now(); | |
| if (now - gameState.lastPowerUpSpawn < gameState.powerUpSpawnDelay) return; | |
| gameState.lastPowerUpSpawn = now; | |
| // 随机生成道具位置 | |
| const x = (Math.random() - 0.5) * 30; | |
| const y = (Math.random() - 0.5) * 10; | |
| const z = 30 + Math.random() * 40; | |
| createPowerUp(x, y, z); | |
| } | |
| // 更新UI | |
| function updateUI() { | |
| // 更新分数 | |
| document.getElementById('score').textContent = `分数: ${gameState.score}`; | |
| // 更新关卡 | |
| document.getElementById('level').textContent = `关卡: ${gameState.level}`; | |
| // 更新生命值 | |
| const healthPercent = (gameState.health / gameState.maxHealth) * 100; | |
| document.getElementById('health-fill').style.width = `${healthPercent}%`; | |
| // 更新弹药 | |
| document.getElementById('ammo').textContent = `弹药: ${gameState.ammo === Infinity ? '∞' : gameState.ammo}`; | |
| // 更新敌人数量 | |
| const enemiesLeft = gameState.enemiesSpawned - gameState.enemiesDestroyed; | |
| document.getElementById('enemy-count').textContent = `敌人: ${enemiesLeft}`; | |
| // 更新特殊武器状态 | |
| const specialWeaponElement = document.getElementById('special-weapon'); | |
| if (specialWeaponElement) { | |
| specialWeaponElement.style.opacity = gameState.specialWeaponReady ? '1' : '0.5'; | |
| } | |
| // 更新Boss血条 | |
| if (gameState.bossActive && gameState.boss) { | |
| updateBossHealth(); | |
| } | |
| } | |
| // 更新Boss血条 | |
| function updateBossHealth() { | |
| if (!gameState.boss || !gameState.bossActive) return; | |
| const boss = gameState.boss.children[0]; | |
| const healthPercent = (boss.userData.health / boss.userData.maxHealth) * 100; | |
| document.getElementById('boss-health-fill').style.width = `${healthPercent}%`; | |
| } | |
| // 显示游戏结束 | |
| function showGameOver() { | |
| document.getElementById('final-score').textContent = gameState.score; | |
| document.getElementById('final-level').textContent = gameState.level; | |
| document.getElementById('game-over').style.display = 'flex'; | |
| gameState.gameOver = true; | |
| } | |
| // 显示关卡完成 | |
| function showLevelComplete() { | |
| document.getElementById('level-score').textContent = gameState.score; | |
| document.getElementById('next-level').textContent = gameState.level + 1; | |
| document.getElementById('level-complete').style.display = 'flex'; | |
| gameState.levelComplete = true; | |
| } | |
| // 应用能量道具效果 | |
| function applyPowerUp(type) { | |
| // 如果已经有相同类型的道具,先清除之前的 | |
| if (gameState.activePowerUps[type]) { | |
| clearTimeout(gameState.activePowerUps[type].timer); | |
| } | |
| // 应用道具效果 | |
| switch (type) { | |
| case 'health': | |
| gameState.health = Math.min(gameState.health + 30, gameState.maxHealth); | |
| updateUI(); | |
| break; | |
| case 'shield': | |
| // 护盾效果在碰撞检测中处理 | |
| break; | |
| case 'speed': | |
| // 速度效果在玩家移动中处理 | |
| break; | |
| case 'fireRate': | |
| // 射击速度效果在射击函数中处理 | |
| break; | |
| case 'special': | |
| gameState.specialWeaponReady = true; | |
| break; | |
| } | |
| // 添加道具图标到UI | |
| addPowerUpIcon(type); | |
| // 设置道具持续时间 | |
| gameState.activePowerUps[type] = { | |
| active: true, | |
| timer: setTimeout(() => { | |
| removePowerUp(type); | |
| }, GAME.POWERUP_DURATION) | |
| }; | |
| } | |
| // 添加道具图标到UI | |
| function addPowerUpIcon(type) { | |
| const powerUpsContainer = document.getElementById('power-ups'); | |
| // 如果已经有相同类型的图标,先移除 | |
| const existingIcon = document.getElementById(`power-up-${type}`); | |
| if (existingIcon) { | |
| powerUpsContainer.removeChild(existingIcon); | |
| } | |
| const icon = document.createElement('div'); | |
| icon.id = `power-up-${type}`; | |
| icon.className = 'power-up-icon'; | |
| // 根据类型设置图标 | |
| switch (type) { | |
| case 'health': | |
| icon.innerHTML = '❤️'; | |
| icon.style.backgroundColor = 'rgba(0, 255, 0, 0.3)'; | |
| break; | |
| case 'shield': | |
| icon.innerHTML = '🛡️'; | |
| icon.style.backgroundColor = 'rgba(0, 0, 255, 0.3)'; | |
| break; | |
| case 'speed': | |
| icon.innerHTML = '⚡'; | |
| icon.style.backgroundColor = 'rgba(255, 165, 0, 0.3)'; | |
| break; | |
| case 'fireRate': | |
| icon.innerHTML = '🔥'; | |
| icon.style.backgroundColor = 'rgba(255, 0, 255, 0.3)'; | |
| break; | |
| case 'special': | |
| icon.innerHTML = '💣'; | |
| icon.style.backgroundColor = 'rgba(255, 0, 0, 0.3)'; | |
| break; | |
| } | |
| powerUpsContainer.appendChild(icon); | |
| // 添加动画效果 | |
| icon.style.transform = 'scale(0)'; | |
| setTimeout(() => { | |
| icon.style.transform = 'scale(1)'; | |
| icon.style.transition = 'transform 0.3s'; | |
| }, 10); | |
| } | |
| // 移除能量道具效果 | |
| function removePowerUp(type) { | |
| if (!gameState.activePowerUps[type]) return; | |
| gameState.activePowerUps[type].active = false; | |
| delete gameState.activePowerUps[type]; | |
| // 从UI中移除图标 | |
| const icon = document.getElementById(`power-up-${type}`); | |
| if (icon) { | |
| icon.style.transform = 'scale(0)'; | |
| setTimeout(() => { | |
| if (icon.parentNode) { | |
| icon.parentNode.removeChild(icon); | |
| } | |
| }, 300); | |
| } | |
| } | |
| // 更新雷达 | |
| function updateRadar() { | |
| // 清除旧的雷达标记 | |
| radarBlips.forEach(blip => { | |
| if (blip.parentNode) { | |
| blip.parentNode.removeChild(blip); | |
| } | |
| }); | |
| radarBlips = []; | |
| // 添加玩家标记 | |
| const playerBlip = document.createElement('div'); | |
| playerBlip.className = 'radar-blip'; | |
| playerBlip.style.backgroundColor = '#00ffff'; | |
| playerBlip.style.left = '50%'; | |
| playerBlip.style.top = '50%'; | |
| document.getElementById('radar').appendChild(playerBlip); | |
| radarBlips.push(playerBlip); | |
| // 添加敌人标记 | |
| gameState.enemies.forEach(enemy => { | |
| // 计算敌人相对于玩家的位置 | |
| const relativeX = enemy.position.x - playerGroup.position.x; | |
| const relativeZ = enemy.position.z - playerGroup.position.z; | |
| // 限制在雷达范围内 | |
| const maxDistance = 50; | |
| const distance = Math.min(Math.sqrt(relativeX * relativeX + relativeZ * relativeZ), maxDistance); | |
| const angle = Math.atan2(relativeX, relativeZ); | |
| // 转换为雷达坐标 | |
| const radarX = 50 + (distance / maxDistance) * 50 * Math.sin(angle); | |
| const radarY = 50 - (distance / maxDistance) * 50 * Math.cos(angle); | |
| // 创建雷达标记 | |
| const blip = document.createElement('div'); | |
| blip.className = 'radar-blip'; | |
| blip.style.left = `${radarX}%`; | |
| blip.style.top = `${radarY}%`; | |
| // Boss标记更大 | |
| if (enemy.userData.isBoss) { | |
| blip.style.width = '10px'; | |
| blip.style.height = '10px'; | |
| } | |
| document.getElementById('radar').appendChild(blip); | |
| radarBlips.push(blip); | |
| }); | |
| } | |
| // 游戏主循环 | |
| function animate() { | |
| requestAnimationFrame(animate); | |
| const delta = clock.getDelta(); | |
| const time = clock.getElapsedTime(); | |
| // 如果游戏结束或关卡完成,不更新游戏状态 | |
| if (gameState.gameOver || gameState.levelComplete) { | |
| effect.render(scene, camera); | |
| return; | |
| } | |
| // 玩家移动 | |
| const speedPower = gameState.activePowerUps['speed']; | |
| const actualPlayerSpeed = speedPower ? GAME.PLAYER_SPEED * 1.5 : GAME.PLAYER_SPEED; | |
| if (keys['KeyW']) playerGroup.position.z += actualPlayerSpeed; | |
| if (keys['KeyS']) playerGroup.position.z -= actualPlayerSpeed; | |
| if (keys['KeyA']) playerGroup.position.x -= actualPlayerSpeed; | |
| if (keys['KeyD']) playerGroup.position.x += actualPlayerSpeed; | |
| if (keys['Space']) playerGroup.position.y += actualPlayerSpeed; | |
| if (keys['ShiftLeft']) playerGroup.position.y -= actualPlayerSpeed; | |
| // 限制玩家移动范围 | |
| playerGroup.position.x = Math.max(-15, Math.min(15, playerGroup.position.x)); | |
| playerGroup.position.y = Math.max(-5, Math.min(5, playerGroup.position.y)); | |
| playerGroup.position.z = Math.max(-15, Math.min(40, playerGroup.position.z)); | |
| // 鼠标射击 | |
| if (isMouseDown) { | |
| fireBullet(); | |
| } | |
| // 生成能量道具 | |
| spawnPowerUp(); | |
| // 更新子弹 | |
| updateBullets(delta); | |
| // 更新敌人 | |
| updateEnemies(delta, time); | |
| // 更新能量道具 | |
| updatePowerUps(delta); | |
| // 检测碰撞 | |
| checkCollisions(); | |
| // 更新雷达 | |
| updateRadar(); | |
| // 更新UI | |
| updateUI(); | |
| // 渲染场景 | |
| effect.render(scene, camera); | |
| } | |
| // 更新子弹 | |
| function updateBullets(delta) { | |
| // 玩家子弹 | |
| for (let i = gameState.bullets.length - 1; i >= 0; i--) { | |
| const bullet = gameState.bullets[i]; | |
| // 移动子弹 | |
| bullet.position.x += bullet.userData.direction.x * bullet.userData.speed * 60 * delta; | |
| bullet.position.y += bullet.userData.direction.y * bullet.userData.speed * 60 * delta; | |
| bullet.position.z += bullet.userData.direction.z * bullet.userData.speed * 60 * delta; | |
| // 移除超出范围的子弹 | |
| if (bullet.position.z > 100 || | |
| bullet.position.z < -50 || | |
| bullet.position.x < -50 || | |
| bullet.position.x > 50 || | |
| bullet.position.y < -20 || | |
| bullet.position.y > 20) { | |
| scene.remove(bullet); | |
| gameState.bullets.splice(i, 1); | |
| } | |
| } | |
| // 敌人子弹 | |
| for (let i = gameState.enemyBullets.length - 1; i >= 0; i--) { | |
| const bullet = gameState.enemyBullets[i]; | |
| // 移动子弹 | |
| bullet.position.x += bullet.userData.direction.x * bullet.userData.speed * 60 * delta; | |
| bullet.position.y += bullet.userData.direction.y * bullet.userData.speed * 60 * delta; | |
| bullet.position.z += bullet.userData.direction.z * bullet.userData.speed * 60 * delta; | |
| // 移除超出范围的子弹 | |
| if (bullet.position.z > 100 || | |
| bullet.position.z < -50 || | |
| bullet.position.x < -50 || | |
| bullet.position.x > 50 || | |
| bullet.position.y < -20 || | |
| bullet.position.y > 20) { | |
| scene.remove(bullet); | |
| gameState.enemyBullets.splice(i, 1); | |
| } | |
| } | |
| } | |
| // 更新敌人 | |
| function updateEnemies(delta, time) { | |
| const currentLevel = Math.min(gameState.level - 1, GAME.LEVELS.length - 1); | |
| const levelData = GAME.LEVELS[currentLevel]; | |
| for (let i = gameState.enemies.length - 1; i >= 0; i--) { | |
| const enemy = gameState.enemies[i]; | |
| // Boss行为 | |
| if (enemy.userData.isBoss) { | |
| // Boss移动模式 | |
| const bossMovePattern = time * 0.5; | |
| enemy.position.x = Math.sin(bossMovePattern) * 10; | |
| enemy.position.y = Math.cos(bossMovePattern * 0.7) * 3; | |
| // Boss射击 | |
| if (time - enemy.userData.lastShot > enemy.userData.shotDelay) { | |
| enemyFireBullet(enemy); | |
| } | |
| } else { | |
| // 普通敌人移动 | |
| enemy.position.z -= enemy.userData.speed * 60 * delta; | |
| // 敌人射击 | |
| if (Math.random() < 0.01 && time - gameState.lastEnemyShot > levelData.spawnRate * 2) { | |
| gameState.lastEnemyShot = time; | |
| enemyFireBullet(enemy); | |
| } | |
| } | |
| // 移除超出范围的敌人 | |
| if (enemy.position.z < -30) { | |
| scene.remove(enemy); | |
| gameState.enemies.splice(i, 1); | |
| // 如果敌人超出范围,减少生命值 | |
| if (!enemy.userData.isBoss) { | |
| gameState.health -= 5; | |
| if (gameState.health <= 0) { | |
| gameState.health = 0; | |
| showGameOver(); | |
| } | |
| updateUI(); | |
| } | |
| } | |
| } | |
| // 检查关卡是否完成 | |
| if (!gameState.bossActive && | |
| gameState.enemiesSpawned >= levelData.enemies && | |
| gameState.enemiesDestroyed >= levelData.enemies) { | |
| // 如果是最后一关,游戏胜利 | |
| if (gameState.level >= GAME.LEVELS.length) { | |
| showGameOver(); // 这里可以改为显示胜利画面 | |
| } else { | |
| showLevelComplete(); | |
| } | |
| } | |
| } | |
| // 更新能量道具 | |
| function updatePowerUps(delta) { | |
| for (let i = gameState.powerUps.length - 1; i >= 0; i--) { | |
| const powerUp = gameState.powerUps[i]; | |
| // 旋转道具 | |
| powerUp.rotation.x += 0.05 * 60 * delta; | |
| powerUp.rotation.y += 0.05 * 60 * delta; | |
| // 上下浮动 | |
| powerUp.position.y += Math.sin(clock.getElapsedTime() * 3) * 0.01 * 60 * delta; | |
| // 向玩家移动 | |
| const direction = new THREE.Vector3( | |
| playerGroup.position.x - powerUp.position.x, | |
| playerGroup.position.y - powerUp.position.y, | |
| playerGroup.position.z - powerUp.position.z | |
| ).normalize(); | |
| powerUp.position.x += direction.x * powerUp.userData.speed * 60 * delta; | |
| powerUp.position.y += direction.y * powerUp.userData.speed * 60 * delta; | |
| powerUp.position.z += direction.z * powerUp.userData.speed * 60 * delta; | |
| // 移除超出范围的子弹 | |
| if (powerUp.position.z > 100 || | |
| powerUp.position.z < -50 || | |
| powerUp.position.x < -50 || | |
| powerUp.position.x > 50 || | |
| powerUp.position.y < -20 || | |
| powerUp.position.y > 20) { | |
| scene.remove(powerUp); | |
| gameState.powerUps.splice(i, 1); | |
| } | |
| } | |
| } | |
| // 检测碰撞 | |
| function checkCollisions() { | |
| // 玩家子弹与敌人碰撞 | |
| for (let i = gameState.bullets.length - 1; i >= 0; i--) { | |
| const bullet = gameState.bullets[i]; | |
| for (let j = gameState.enemies.length - 1; j >= 0; j--) { | |
| const enemy = gameState.enemies[j]; | |
| const enemyMesh = enemy.children[0]; | |
| // 简单的距离检测碰撞 | |
| const distance = bullet.position.distanceTo(enemyMesh.position); | |
| if (distance < enemyMesh.geometry.boundingSphere.radius + 0.2) { | |
| // 敌人受伤 | |
| enemyMesh.userData.health--; | |
| // 根据敌人血量改变颜色 | |
| const healthPercent = enemyMesh.userData.health / enemyMesh.userData.maxHealth; | |
| if (healthPercent < 0.33) { | |
| enemyMesh.material.color.setHex(0xff0000); | |
| enemyMesh.material.emissive.setHex(0x990000); | |
| } else if (healthPercent < 0.66) { | |
| enemyMesh.material.color.setHex(0xff9900); | |
| enemyMesh.material.emissive.setHex(0x994400); | |
| } | |
| // 移除子弹 | |
| scene.remove(bullet); | |
| gameState.bullets.splice(i, 1); | |
| // 如果敌人死亡 | |
| if (enemyMesh.userData.health <= 0) { | |
| // 增加分数 | |
| const points = enemy.userData.isBoss ? 500 : 100 * gameState.level; | |
| gameState.score += points; | |
| // 随机掉落能量道具 | |
| if (Math.random() < 0.3 || enemy.userData.isBoss) { | |
| createPowerUp(enemy.position.x, enemy.position.y, enemy.position.z); | |
| } | |
| // 移除敌人 | |
| scene.remove(enemy); | |
| gameState.enemies.splice(j, 1); | |
| gameState.enemiesDestroyed++; | |
| // 如果是Boss | |
| if (enemy.userData.isBoss) { | |
| gameState.bossActive = false; | |
| gameState.boss = null; | |
| document.getElementById('boss-health').style.display = 'none'; | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| } | |
| // 敌人子弹与玩家碰撞 | |
| for (let i = gameState.enemyBullets.length - 1; i >= 0; i--) { | |
| const bullet = gameState.enemyBullets[i]; | |
| // 简单的距离检测碰撞 | |
| const distance = bullet.position.distanceTo(playerGroup.position); | |
| if (distance < 1) { | |
| // 如果有护盾,不受伤 | |
| if (!gameState.activePowerUps['shield']) { | |
| gameState.health -= 10; | |
| if (gameState.health <= 0) { | |
| gameState.health = 0; | |
| showGameOver(); | |
| } | |
| } | |
| // 移除子弹 | |
| scene.remove(bullet); | |
| gameState.enemyBullets.splice(i, 1); | |
| } | |
| } | |
| // 能量道具与玩家碰撞 | |
| for (let i = gameState.powerUps.length - 1; i >= 0; i--) { | |
| const powerUp = gameState.powerUps[i]; | |
| // 简单的距离检测碰撞 | |
| const distance = powerUp.position.distanceTo(playerGroup.position); | |
| if (distance < 1) { | |
| // 应用道具效果 | |
| applyPowerUp(powerUp.children[0].userData.type); | |
| // 移除道具 | |
| scene.remove(powerUp); | |
| gameState.powerUps.splice(i, 1); | |
| } | |
| } | |
| // 敌人与玩家碰撞 | |
| for (let i = gameState.enemies.length - 1; i >= 0; i--) { | |
| const enemy = gameState.enemies[i]; | |
| const enemyMesh = enemy.children[0]; | |
| // 简单的距离检测碰撞 | |
| const distance = enemyMesh.position.distanceTo(playerGroup.position); | |
| if (distance < enemyMesh.geometry.boundingSphere.radius + 0.5) { | |
| // 如果有护盾,不受伤 | |
| if (!gameState.activePowerUps['shield']) { | |
| gameState.health -= 20; | |
| if (gameState.health <= 0) { | |
| gameState.health = 0; | |
| showGameOver(); | |
| } | |
| } | |
| // 敌人受伤 | |
| enemyMesh.userData.health -= 5; | |
| // 如果敌人死亡 | |
| if (enemyMesh.userData.health <= 0) { | |
| // 增加分数 | |
| const points = enemy.userData.isBoss ? 500 : 100 * gameState.level; | |
| gameState.score += points; | |
| // 随机掉落能量道具 | |
| if (Math.random() < 0.3 || enemy.userData.isBoss) { | |
| createPowerUp(enemy.position.x, enemy.position.y, enemy.position.z); | |
| } | |
| // 移除敌人 | |
| scene.remove(enemy); | |
| gameState.enemies.splice(i, 1); | |
| gameState.enemiesDestroyed++; | |
| // 如果是Boss | |
| if (enemy.userData.isBoss) { | |
| gameState.bossActive = false; | |
| gameState.boss = null; | |
| document.getElementById('boss-health').style.display = 'none'; | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| } | |
| // 启动游戏 | |
| init(); | |
| </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=CYXiaofeng/fly-game" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |