<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>AREMM - 升级版</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
touch-action: none;
user-select: none;
-webkit-tap-highlight-color: transparent;
font-family: 'Arial', sans-serif;
}
body {
overflow: hidden;
background: linear-gradient(135deg, #1a2a6c, #b21f1f, #1a2a6c);
color: white;
}
#game-container {
position: relative;
width: 100%;
height: 100vh;
}
#splash-screen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: radial-gradient(circle, #0c1445 0%, #020617 100%);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 100;
transition: opacity 1s ease-in-out;
}
.splash-logo {
font-size: 6rem;
font-weight: bold;
color: #ff9900;
text-shadow: 0 0 20px rgba(255, 153, 0, 0.7);
margin-bottom: 1rem;
animation: pulse 2s infinite;
}
.studio-logo {
font-size: 2rem;
color: #4dc3ff;
margin-bottom: 3rem;
}
.start-button {
padding: 15px 40px;
font-size: 1.5rem;
background: linear-gradient(45deg, #ff512f, #f09819);
border: none;
border-radius: 50px;
color: white;
cursor: pointer;
transition: all 0.3s;
box-shadow: 0 5px 15px rgba(240, 152, 25, 0.4);
}
.start-button:hover {
transform: scale(1.05);
box-shadow: 0 8px 20px rgba(240, 152, 25, 0.6);
}
#game-canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
#ui-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
.ui-panel {
background: rgba(0, 0, 0, 0.6);
border: 2px solid rgba(255, 153, 0, 0.5);
border-radius: 10px;
padding: 15px;
margin: 15px;
backdrop-filter: blur(5px);
}
#top-left-panel {
position: absolute;
top: 20px;
left: 20px;
width: 250px;
}
#bottom-right-panel {
position: absolute;
bottom: 20px;
right: 20px;
width: 200px;
}
#center-panel {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
display: none;
}
.stats-row {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
}
.stats-label {
color: #4dc3ff;
}
.stats-value {
color: #ff9900;
font-weight: bold;
}
.button {
background: linear-gradient(45deg, #4dc3ff, #1a75ff);
border: none;
border-radius: 5px;
color: white;
padding: 8px 15px;
margin: 5px;
cursor: pointer;
pointer-events: auto;
transition: all 0.2s;
}
.button:hover {
background: linear-gradient(45deg, #1a75ff, #4dc3ff);
transform: translateY(-2px);
}
#exit-vehicle-btn {
background: linear-gradient(45deg, #ff512f, #f09819);
display: none;
}
#shop-btn {
background: linear-gradient(45deg, #8E2DE2, #4A00E0);
}
.shop-item {
background: rgba(50, 50, 100, 0.7);
border-radius: 8px;
padding: 10px;
margin: 10px 0;
cursor: pointer;
pointer-events: auto;
transition: all 0.2s;
}
.shop-item:hover {
background: rgba(70, 70, 150, 0.8);
transform: scale(1.02);
}
.item-name {
color: #ff9900;
font-weight: bold;
}
.item-price {
color: #4dc3ff;
font-size: 0.9rem;
}
.item-desc {
color: #ccc;
font-size: 0.85rem;
margin-top: 5px;
}
.weapon-slot {
display: inline-block;
width: 40px;
height: 40px;
background: rgba(100, 100, 100, 0.6);
border: 2px solid #4dc3ff;
border-radius: 5px;
margin: 5px;
text-align: center;
line-height: 40px;
font-size: 1.2rem;
pointer-events: auto;
cursor: pointer;
}
.active-slot {
border-color: #ff9900;
background: rgba(255, 153, 0, 0.2);
}
.zombie-indicator {
position: absolute;
width: 10px;
height: 10px;
background: #ff0000;
border-radius: 50%;
box-shadow: 0 0 10px rgba(255, 0, 0, 0.7);
}
.teammate-indicator {
position: absolute;
width: 8px;
height: 8px;
background: #4dc3ff;
border-radius: 50%;
box-shadow: 0 0 10px rgba(77, 195, 255, 0.7);
}
.health-bar {
width: 100%;
height: 15px;
background: rgba(100, 0, 0, 0.5);
border-radius: 5px;
margin-top: 10px;
overflow: hidden;
}
.health-fill {
height: 100%;
background: linear-gradient(90deg, #ff0000, #ff9900);
width: 100%;
transition: width 0.3s;
}
.message {
position: absolute;
top: 20%;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.7);
padding: 15px 30px;
border-radius: 10px;
border-left: 5px solid #ff9900;
display: none;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.fade-in {
animation: fadeIn 0.5s ease-in;
}
</style>
</head>
<body>
<div id="game-container">
<div id="splash-screen" class="fade-in">
<div class="splash-logo">AREMM</div>
<div class="studio-logo">鼠神工作室</div>
<button class="start-button" id="start-btn">开始游戏</button>
</div>
<canvas id="game-canvas"></canvas>
<div id="ui-container">
<div id="top-left-panel" class="ui-panel">
<div class="stats-row">
<span class="stats-label">生命值:</span>
<span class="stats-value" id="health-value">100</span>
</div>
<div class="health-bar">
<div class="health-fill" id="health-bar"></div>
</div>
<div class="stats-row">
<span class="stats-label">速度:</span>
<span class="stats-value" id="speed-value">0 km/h</span>
</div>
<div class="stats-row">
<span class="stats-label">弹药:</span>
<span class="stats-value" id="ammo-value">30</span>
</div>
<div class="stats-row">
<span class="stats-label">积分:</span>
<span class="stats-value" id="points-value">500</span>
</div>
<div style="margin-top: 15px;">
<div>武器槽:</div>
<div>
<div class="weapon-slot active-slot" data-weapon="pistol">P</div>
<div class="weapon-slot" data-weapon="shotgun">S</div>
<div class="weapon-slot" data-weapon="rifle">R</div>
</div>
</div>
</div>
<div id="bottom-right-panel" class="ui-panel">
<button class="button" id="exit-vehicle-btn">下车</button>
<button class="button" id="shop-btn">商店</button>
<div id="shop-panel" style="display: none; margin-top: 15px;">
<div class="shop-item">
<div class="item-name">医疗包</div>
<div class="item-price">100 积分</div>
<div class="item-desc">恢复 50 点生命值</div>
</div>
<div class="shop-item">
<div class="item-name">弹药包</div>
<div class="item-price">80 积分</div>
<div class="item-desc">补充所有武器弹药</div>
</div>
<div class="shop-item">
<div class="item-name">霰弹枪</div>
<div class="item-price">300 积分</div>
<div class="item-desc">近距离高伤害武器</div>
</div>
<div class="shop-item">
<div class="item-name">防御塔</div>
<div class="item-price">500 积分</div>
<div class="item-desc">自动攻击附近僵尸</div>
</div>
</div>
</div>
<div id="center-panel" class="ui-panel">
<h2>游戏结束</h2>
<p>你的分数: <span id="final-score">0</span></p>
<button class="button" id="restart-btn">重新开始</button>
</div>
<div class="message" id="game-message"></div>
</div>
</div>
<script>
// 游戏状态管理
const GameState = {
SPLASH: 0,
PLAYING: 1,
IN_VEHICLE: 2,
SHOPPING: 3,
GAME_OVER: 4
};
// 游戏配置
const GameConfig = {
playerSpeed: 0.2,
vehicleSpeed: 0.5,
playerHealth: 100,
zombieDamage: 10,
zombieSpawnRate: 2000, // 每2秒生成一个僵尸
teammateCount: 3,
bulletSpeed: 1,
bulletDamage: 25
};
// 游戏主类
class AREMMGame {
constructor() {
this.state = GameState.SPLASH;
this.clock = new THREE.Clock();
this.scene = new THREE.Scene();
this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
this.renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('game-canvas'), antialias: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.setClearColor(0x87CEEB);
this.player = null;
this.vehicle = null;
this.zombies = [];
this.teammates = [];
this.bullets = [];
this.controls = {
forward: false,
backward: false,
left: false,
right: false,
shoot: false
};
this.stats = {
health: GameConfig.playerHealth,
ammo: 30,
points: 500,
kills: 0
};
this.init();
this.setupEventListeners();
}
init() {
// 设置相机位置
this.camera.position.set(0, 10, 15);
this.camera.lookAt(0, 0, 0);
// 添加光源
const ambientLight = new THREE.AmbientLight(0x606060);
this.scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(1, 1, 0.5).normalize();
this.scene.add(directionalLight);
// 创建地面
const groundGeometry = new THREE.PlaneGeometry(100, 100);
const groundMaterial = new THREE.MeshStandardMaterial({
color: 0x2E8B57,
roughness: 0.8
});
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.rotation.x = -Math.PI / 2;
this.scene.add(ground);
// 添加一些随机障碍物
for (let i = 0; i < 20; i++) {
const size = Math.random() * 3 + 1;
const geometry = new THREE.BoxGeometry(size, size, size);
const material = new THREE.MeshStandardMaterial({
color: Math.random() * 0xffffff,
roughness: 0.7
});
const obstacle = new THREE.Mesh(geometry, material);
obstacle.position.set(
Math.random() * 80 - 40,
size / 2,
Math.random() * 80 - 40
);
this.scene.add(obstacle);
}
// 创建玩家
this.createPlayer();
// 创建车辆
this.createVehicle();
// 创建队友
this.createTeammates();
// 开始游戏循环
this.animate();
}
createPlayer() {
const geometry = new THREE.CapsuleGeometry(0.5, 1, 4, 8);
const material = new THREE.MeshStandardMaterial({ color: 0x4169E1 });
this.player = new THREE.Mesh(geometry, material);
this.player.position.set(0, 1.5, 0);
this.scene.add(this.player);
// 添加玩家武器
const weaponGeometry = new THREE.BoxGeometry(0.3, 0.1, 1);
const weaponMaterial = new THREE.MeshStandardMaterial({ color: 0x333333 });
this.playerWeapon = new THREE.Mesh(weaponGeometry, weaponMaterial);
this.playerWeapon.position.set(0.5, 0.2, 0.5);
this.player.add(this.playerWeapon);
}
createVehicle() {
// 创建车体
const bodyGeometry = new THREE.BoxGeometry(3, 1, 5);
const bodyMaterial = new THREE.MeshStandardMaterial({ color: 0xFF4500 });
const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
// 创建车轮
const wheelGeometry = new THREE.CylinderGeometry(0.5, 0.5, 0.3, 16);
wheelGeometry.rotateZ(Math.PI / 2);
const wheelMaterial = new THREE.MeshStandardMaterial({ color: 0x222222 });
const wheelPositions = [
{ x: 1.2, y: -0.5, z: 1.8 },
{ x: -1.2, y: -0.5, z: 1.8 },
{ x: 1.2, y: -0.5, z: -1.8 },
{ x: -1.2, y: -0.5, z: -1.8 }
];
this.vehicle = new THREE.Group();
this.vehicle.add(body);
wheelPositions.forEach(pos => {
const wheel = new THREE.Mesh(wheelGeometry, wheelMaterial);
wheel.position.set(pos.x, pos.y, pos.z);
this.vehicle.add(wheel);
});
this.vehicle.position.set(10, 1, 0);
this.vehicle.rotation.y = Math.PI / 2;
this.scene.add(this.vehicle);
this.vehicleSpeed = 0;
this.vehicleDirection = new THREE.Vector3();
}
createTeammates() {
for (let i = 0; i < GameConfig.teammateCount; i++) {
const geometry = new THREE.CapsuleGeometry(0.4, 0.8, 4, 8);
const material = new THREE.MeshStandardMaterial({ color: 0x32CD32 });
const teammate = new THREE.Mesh(geometry, material);
// 随机位置
const angle = (i / GameConfig.teammateCount) * Math.PI * 2;
const radius = 10;
teammate.position.set(
Math.cos(angle) * radius,
0.8,
Math.sin(angle) * radius
);
this.scene.add(teammate);
this.teammates.push({
mesh: teammate,
target: new THREE.Vector3(),
state: 'patrol', // patrol, attack, follow
reloadTime: 0
});
}
}
spawnZombie() {
const geometry = new THREE.CapsuleGeometry(0.5, 1, 4, 8);
const material = new THREE.MeshStandardMaterial({ color: 0x8B0000 });
const zombie = new THREE.Mesh(geometry, material);
// 在玩家一定距离外生成僵尸
const angle = Math.random() * Math.PI * 2;
const distance = 30 + Math.random() * 20;
zombie.position.set(
Math.cos(angle) * distance,
1,
Math.sin(angle) * distance
);
this.scene.add(zombie);
this.zombies.push({
mesh: zombie,
health: 100
});
}
enterVehicle() {
if (this.state === GameState.PLAYING) {
// 检查玩家是否在车辆附近
const distance = this.player.position.distanceTo(this.vehicle.position);
if (distance < 5) {
this.state = GameState.IN_VEHICLE;
document.getElementById('exit-vehicle-btn').style.display = 'block';
// 隐藏玩家
this.player.visible = false;
// 将相机附加到车辆
this.camera.position.set(0, 3, -7);
this.camera.lookAt(0, 0, 10);
this.vehicle.add(this.camera);
this.showMessage("已进入车辆,使用WASD控制");
}
}
}
exitVehicle() {
if (this.state === GameState.IN_VEHICLE) {
this.state = GameState.PLAYING;
document.getElementById('exit-vehicle-btn').style.display = 'none';
// 显示玩家
this.player.visible = true;
// 将玩家放在车辆旁边
this.scene.add(this.camera);
const vehicleDirection = new THREE.Vector3();
this.vehicle.getWorldDirection(vehicleDirection);
vehicleDirection.multiplyScalar(3);
this.player.position.copy(this.vehicle.position).add(vehicleDirection);
this.player.position.y = 1.5;
// 重置相机位置
this.camera.position.set(0, 10, 15);
this.camera.lookAt(this.player.position);
}
}
toggleShop() {
if (this.state === GameState.PLAYING || this.state === GameState.IN_VEHICLE) {
const shopPanel = document.getElementById('shop-panel');
if (this.state === GameState.SHOPPING) {
this.state = this.previousState;
shopPanel.style.display = 'none';
} else {
this.previousState = this.state;
this.state = GameState.SHOPPING;
shopPanel.style.display = 'block';
}
}
}
buyItem(itemName) {
let cost = 0;
switch(itemName) {
case '医疗包':
cost = 100;
if (this.stats.points >= cost) {
this.stats.points -= cost;
this.stats.health = Math.min(100, this.stats.health + 50);
this.updateUI();
this.showMessage("已购买医疗包,生命值恢复50点");
} else {
this.showMessage("积分不足!");
}
break;
case '弹药包':
cost = 80;
if (this.stats.points >= cost) {
this.stats.points -= cost;
this.stats.ammo = 30;
this.updateUI();
this.showMessage("已购买弹药包,弹药已补充");
} else {
this.showMessage("积分不足!");
}
break;
case '霰弹枪':
cost = 300;
if (this.stats.points >= cost) {
this.stats.points -= cost;
this.showMessage("已购买霰弹枪,按2键切换");
} else {
this.showMessage("积分不足!");
}
break;
case '防御塔':
cost = 500;
if (this.stats.points >= cost) {
this.stats.points -= cost;
this.showMessage("已购买防御塔,将在基地部署");
} else {
this.showMessage("积分不足!");
}
break;
}
}
shoot() {
if (this.state !== GameState.PLAYING || this.stats.ammo <= 0) return;
this.stats.ammo--;
this.updateUI();
// 创建子弹
const geometry = new THREE.SphereGeometry(0.1, 8, 8);
const material = new THREE.MeshBasicMaterial({ color: 0xFFFF00 });
const bullet = new THREE.Mesh(geometry, material);
// 设置子弹位置和方向
bullet.position.copy(this.player.position);
bullet.position.y += 1;
const direction = new THREE.Vector3();
this.camera.getWorldDirection(direction);
direction.multiplyScalar(GameConfig.bulletSpeed);
this.scene.add(bullet);
this.bullets.push({
mesh: bullet,
velocity: direction,
damage: GameConfig.bulletDamage
});
// 添加射击效果
this.createMuzzleFlash();
}
createMuzzleFlash() {
const flashGeometry = new THREE.SphereGeometry(0.2, 8, 8);
const flashMaterial = new THREE.MeshBasicMaterial({
color: 0xFF9900,
transparent: true,
opacity: 0.8
});
const flash = new THREE.Mesh(flashGeometry, flashMaterial);
flash.position.copy(this.player.position);
flash.position.y += 1;
flash.position.add(this.playerWeapon.getWorldPosition(new THREE.Vector3()));
this.scene.add(flash);
// 短暂显示后移除
setTimeout(() => {
this.scene.remove(flash);
}, 50);
}
updatePlayer(delta) {
if (this.state !== GameState.PLAYING) return;
const speed = GameConfig.playerSpeed * delta * 60;
const direction = new THREE.Vector3();
if (this.controls.forward) direction.z -= speed;
if (this.controls.backward) direction.z += speed;
if (this.controls.left) direction.x -= speed;
if (this.controls.right) direction.x += speed;
// 应用移动
direction.applyQuaternion(this.camera.quaternion);
direction.y = 0;
this.player.position.add(direction);
// 更新相机位置
const targetPosition = this.player.position.clone();
targetPosition.y += 10;
targetPosition.z += 15;
this.camera.position.lerp(targetPosition, 0.1);
this.camera.lookAt(this.player.position);
}
updateVehicle(delta) {
if (this.state !== GameState.IN_VEHICLE) return;
const speed = GameConfig.vehicleSpeed * delta * 60;
this.vehicleDirection.set(0, 0, 0);
if (this.controls.forward) this.vehicleDirection.z -= speed;
if (this.controls.backward) this.vehicleDirection.z += speed;
if (this.controls.left) this.vehicle.rotation.y += 0.03;
if (this.controls.right) this.vehicle.rotation.y -= 0.03;
// 应用移动方向
this.vehicleDirection.applyQuaternion(this.vehicle.quaternion);
this.vehicle.position.add(this.vehicleDirection);
// 更新速度显示
this.vehicleSpeed = this.vehicleDirection.length() * 20;
document.getElementById('speed-value').textContent =
Math.round(this.vehicleSpeed) + ' km/h';
}
updateTeammates(delta) {
this.teammates.forEach(teammate => {
// 队友AI逻辑
switch(teammate.state) {
case 'patrol':
// 随机巡逻
if (Math.random() < 0.02) {
teammate.target.set(
Math.random() * 80 - 40,
0,
Math.random() * 80 - 40
);
}
// 寻找僵尸
for (const zombie of this.zombies) {
const distance = teammate.mesh.position.distanceTo(zombie.mesh.position);
if (distance < 15) {
teammate.state = 'attack';
teammate.target = zombie;
break;
}
}
break;
case 'attack':
if (!teammate.target || teammate.target.health <= 0) {
teammate.state = 'patrol';
break;
}
// 朝僵尸移动
const direction = new THREE.Vector3();
direction.subVectors(teammate.target.mesh.position, teammate.mesh.position);
direction.y = 0;
direction.normalize().multiplyScalar(0.05 * delta * 60);
if (teammate.mesh.position.distanceTo(teammate.target.mesh.position) > 5) {
teammate.mesh.position.add(direction);
teammate.mesh.lookAt(teammate.target.mesh.position);
} else {
// 攻击僵尸
if (teammate.reloadTime <= 0) {
teammate.target.health -= 15;
teammate.reloadTime = 1.0; // 1秒装弹时间
if (teammate.target.health <= 0) {
this.scene.remove(teammate.target.mesh);
this.zombies = this.zombies.filter(z => z !== teammate.target);
teammate.state = 'patrol';
this.stats.points += 20;
this.stats.kills++;
this.updateUI();
}
}
}
break;
}
// 更新装弹时间
if (teammate.reloadTime > 0) {
teammate.reloadTime -= delta;
}
// 朝向目标
if (teammate.state === 'patrol') {
const lookDirection = new THREE.Vector3();
lookDirection.subVectors(teammate.target, teammate.mesh.position);
lookDirection.y = 0;
if (lookDirection.length() > 0.1) {
teammate.mesh.lookAt(teammate.target);
}
}
});
}
updateZombies(delta) {
// 生成新僵尸
if (this.zombieSpawnTimer <= 0) {
this.spawnZombie();
this.zombieSpawnTimer = GameConfig.zombieSpawnRate / 1000;
} else {
this.zombieSpawnTimer -= delta;
}
// 更新僵尸行为
this.zombies.forEach(zombie => {
// 寻找最近的目标(玩家或队友)
let target = this.player;
let minDistance = zombie.mesh.position.distanceTo(this.player.position);
for (const teammate of this.teammates) {
const distance = zombie.mesh.position.distanceTo(teammate.mesh.position);
if (distance < minDistance) {
minDistance = distance;
target = teammate.mesh;
}
}
// 朝目标移动
const direction = new THREE.Vector3();
direction.subVectors(target.position, zombie.mesh.position);
direction.y = 0;
direction.normalize().multiplyScalar(0.03 * delta * 60);
zombie.mesh.position.add(direction);
// 朝向目标
zombie.mesh.lookAt(target.position);
// 攻击逻辑
if (minDistance < 1.5) {
if (target === this.player) {
this.stats.health -= GameConfig.zombieDamage * delta;
this.updateUI();
if (this.stats.health <= 0) {
this.gameOver();
}
} else {
// 攻击队友(简化处理)
if (Math.random() < 0.1) {
const teammate = this.teammates.find(t => t.mesh === target);
if (teammate) {
// 队友被攻击(简化处理)
}
}
}
}
});
}
updateBullets(delta) {
for (let i = this.bullets.length - 1; i >= 0; i--) {
const bullet = this.bullets[i];
bullet.mesh.position.add(bullet.velocity);
// 检查碰撞
let hit = false;
// 检查与僵尸的碰撞
for (const zombie of this.zombies) {
if (bullet.mesh.position.distanceTo(zombie.mesh.position) < 1) {
zombie.health -= bullet.damage;
if (zombie.health <= 0) {
this.scene.remove(zombie.mesh);
this.zombies.splice(this.zombies.indexOf(zombie), 1);
this.stats.points += 50;
this.stats.kills++;
this.updateUI();
}
hit = true;
break;
}
}
// 检查超出边界
if (bullet.mesh.position.length() > 100 || hit) {
this.scene.remove(bullet.mesh);
this.bullets.splice(i, 1);
}
}
}
updateUI() {
document.getElementById('health-value').textContent = Math.floor(this.stats.health);
document.getElementById('health-bar').style.width = this.stats.health + '%';
document.getElementById('ammo-value').textContent = this.stats.ammo;
document.getElementById('points-value').textContent = this.stats.points;
}
showMessage(text) {
const message = document.getElementById('game-message');
message.textContent = text;
message.style.display = 'block';
setTimeout(() => {
message.style.display = 'none';
}, 3000);
}
gameOver() {
this.state = GameState.GAME_OVER;
document.getElementById('final-score').textContent = this.stats.kills * 100 + this.stats.points;
document.getElementById('center-panel').style.display = 'block';
}
restartGame() {
// 重置游戏状态
this.state = GameState.PLAYING;
this.stats = {
health: GameConfig.playerHealth,
ammo: 30,
points: 500,
kills: 0
};
// 移除所有僵尸
this.zombies.forEach(zombie => this.scene.remove(zombie.mesh));
this.zombies = [];
// 移除所有子弹
this.bullets.forEach(bullet => this.scene.remove(bullet.mesh));
this.bullets = [];
// 重置玩家位置
this.player.position.set(0, 1.5, 0);
this.player.visible = true;
// 重置车辆位置
this.vehicle.position.set(10, 1, 0);
this.vehicle.rotation.y = Math.PI / 2;
// 重置相机
this.scene.add(this.camera);
this.camera.position.set(0, 10, 15);
this.camera.lookAt(0, 0, 0);
// 更新UI
this.updateUI();
document.getElementById('center-panel').style.display = 'none';
document.getElementById('exit-vehicle-btn').style.display = 'none';
}
setupEventListeners() {
// 键盘控制
window.addEventListener('keydown', (e) => {
switch(e.key.toLowerCase()) {
case 'w': this.controls.forward = true; break;
case 's': this.controls.backward = true; break;
case 'a': this.controls.left = true; break;
case 'd': this.controls.right = true; break;
case 'e': this.enterVehicle(); break;
case ' ':
if (this.state === GameState.PLAYING) this.shoot();
break;
case '1':
case '2':
case '3':
this.selectWeapon(parseInt(e.key) - 1);
break;
}
});
window.addEventListener('keyup', (e) => {
switch(e.key.toLowerCase()) {
case 'w': this.controls.forward = false; break;
case 's': this.controls.backward = false; break;
case 'a': this.controls.left = false; break;
case 'd': this.controls.right = false; break;
}
});
// 鼠标控制
window.addEventListener('mousedown', () => {
if (this.state === GameState.PLAYING) this.controls.shoot = true;
});
window.addEventListener('mouseup', () => {
this.controls.shoot = false;
});
// 按钮事件
document.getElementById('start-btn').addEventListener('click', () => {
document.getElementById('splash-screen').style.opacity = 0;
setTimeout(() => {
document.getElementById('splash-screen').style.display = 'none';
this.state = GameState.PLAYING;
}, 1000);
});
document.getElementById('exit-vehicle-btn').addEventListener('click', () => {
this.exitVehicle();
});
document.getElementById('shop-btn').addEventListener('click', () => {
this.toggleShop();
});
document.getElementById('restart-btn').addEventListener('click', () => {
this.restartGame();
});
// 商店购买
document.querySelectorAll('.shop-item').forEach(item => {
item.addEventListener('click', () => {
const itemName = item.querySelector('.item-name').textContent;
this.buyItem(itemName);
});
});
// 武器选择
document.querySelectorAll('.weapon-slot').forEach((slot, index) => {
slot.addEventListener('click', () => {
this.selectWeapon(index);
});
});
// 窗口大小调整
window.addEventListener('resize', () => {
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
});
}
selectWeapon(index) {
document.querySelectorAll('.weapon-slot').forEach((slot, i) => {
if (i === index) {
slot.classList.add('active-slot');
} else {
slot.classList.remove('active-slot');
}
});
// 在实际游戏中这里会切换武器模型和属性
this.showMessage(`已切换武器: ${['手枪', '霰弹枪', '步枪'][index]}`);
}
animate() {
requestAnimationFrame(() => this.animate());
const delta = Math.min(0.1, this.clock.getDelta());
// 根据游戏状态更新
switch(this.state) {
case GameState.PLAYING:
this.updatePlayer(delta);
this.updateTeammates(delta);
this.updateZombies(delta);
this.updateBullets(delta);
break;
case GameState.IN_VEHICLE:
this.updateVehicle(delta);
this.updateTeammates(delta);
this.updateZombies(delta);
break;
}
// 渲染场景
this.renderer.render(this.scene, this.camera);
}
}
// 初始化游戏
window.addEventListener('load', () => {
const game = new AREMMGame();
});
</script>
</body>
</html>
index.html
style.css
index.js
index.html