<!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;
}
body {
overflow: hidden;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #0c1a2d, #1a3a5f);
color: #fff;
height: 100vh;
display: flex;
flex-direction: column;
}
#game-container {
position: relative;
width: 100%;
height: 100vh;
overflow: hidden;
}
#game-canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
}
#ui-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 10;
pointer-events: none;
}
/* 优化摇杆区域 */
#touch-joystick {
position: absolute;
bottom: 30px;
left: 30px;
width: 140px;
height: 140px;
background: rgba(0, 0, 0, 0.3);
border-radius: 50%;
border: 2px solid rgba(255, 255, 255, 0.5);
pointer-events: auto;
display: flex;
justify-content: center;
align-items: center;
backdrop-filter: blur(5px);
}
#joystick-knob {
width: 60px;
height: 60px;
background: rgba(255, 255, 255, 0.9);
border-radius: 50%;
box-shadow: 0 0 10px rgba(0, 150, 255, 0.7);
pointer-events: none;
transition: transform 0.1s;
}
/* 优化射击按钮 */
#shoot-button {
position: absolute;
bottom: 40px;
right: 40px;
width: 110px;
height: 110px;
background: radial-gradient(circle, rgba(255,50,50,0.8) 0%, rgba(180,20,20,0.6) 70%);
border-radius: 50%;
border: 2px solid rgba(255, 255, 255, 0.8);
pointer-events: auto;
display: flex;
justify-content: center;
align-items: center;
font-size: 28px;
color: white;
font-weight: bold;
text-shadow: 0 0 5px rgba(0,0,0,0.7);
box-shadow: 0 0 20px rgba(255, 50, 50, 0.7);
transition: transform 0.1s, box-shadow 0.1s;
backdrop-filter: blur(2px);
z-index: 20;
}
#shoot-button:active {
transform: scale(0.95);
box-shadow: 0 0 10px rgba(255, 50, 50, 0.5);
}
#health-bar {
position: absolute;
top: 20px;
left: 20px;
width: 220px;
height: 35px;
background: rgba(0, 0, 0, 0.4);
border: 2px solid #333;
border-radius: 10px;
overflow: hidden;
pointer-events: none;
box-shadow: 0 0 10px rgba(0,0,0,0.5);
backdrop-filter: blur(2px);
}
#health-fill {
height: 100%;
width: 100%;
background: linear-gradient(90deg, #ff3300, #ff9900, #5eff00);
transition: width 0.3s;
border-radius: 8px;
}
#health-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 18px;
font-weight: bold;
color: white;
text-shadow: 1px 1px 2px rgba(0,0,0,0.8);
}
#ammo-display {
position: absolute;
top: 65px;
left: 20px;
font-size: 24px;
color: #fff;
text-shadow: 0 0 5px rgba(0,0,0,0.5);
pointer-events: none;
background: rgba(0, 0, 0, 0.4);
padding: 5px 15px;
border-radius: 10px;
font-weight: bold;
backdrop-filter: blur(2px);
}
#score-display {
position: absolute;
top: 20px;
right: 20px;
font-size: 28px;
color: #fff;
text-shadow: 0 0 5px rgba(0,0,0,0.5);
pointer-events: none;
background: rgba(0, 0, 0, 0.4);
padding: 5px 15px;
border-radius: 10px;
font-weight: bold;
backdrop-filter: blur(2px);
}
#game-title {
position: absolute;
top: 20px;
left: 50%;
transform: translateX(-50%);
font-size: 36px;
color: #ffcc00;
text-shadow: 0 0 15px rgba(255, 100, 0, 0.9);
text-align: center;
pointer-events: none;
font-weight: bold;
background: linear-gradient(to right, #ff9900, #ffcc00);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
letter-spacing: 1px;
}
#game-message {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 36px;
color: #ffcc00;
text-shadow: 0 0 10px rgba(0,0,0,0.8);
text-align: center;
background: rgba(0, 0, 0, 0.85);
padding: 25px 50px;
border-radius: 20px;
border: 2px solid #ff9900;
pointer-events: none;
display: none;
z-index: 20;
box-shadow: 0 0 30px rgba(255, 100, 0, 0.7);
backdrop-filter: blur(5px);
}
/* 优化装弹按钮 */
#reload-button {
position: absolute;
bottom: 40px;
right: 170px;
width: 80px;
height: 80px;
background: radial-gradient(circle, rgba(50,100,255,0.8) 0%, rgba(20,60,180,0.6) 70%);
border-radius: 50%;
border: 2px solid rgba(255, 255, 255, 0.8);
pointer-events: auto;
display: flex;
justify-content: center;
align-items: center;
font-size: 16px;
color: white;
font-weight: bold;
text-align: center;
padding: 5px;
box-shadow: 0 0 15px rgba(50, 100, 255, 0.6);
transition: transform 0.1s;
backdrop-filter: blur(2px);
z-index: 20;
}
#reload-button:active {
transform: scale(0.95);
}
.crosshair {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 40px;
height: 40px;
pointer-events: none;
z-index: 15;
}
.crosshair::before, .crosshair::after {
content: '';
position: absolute;
background: rgba(255, 50, 50, 0.9);
box-shadow: 0 0 5px rgba(255, 0, 0, 0.8);
}
.crosshair::before {
width: 4px;
height: 25px;
top: 7.5px;
left: 18px;
}
.crosshair::after {
width: 25px;
height: 4px;
top: 18px;
left: 7.5px;
}
.enemy-hp {
position: absolute;
background: linear-gradient(90deg, #ff3300, #ff9900);
height: 5px;
width: 50px;
border-radius: 3px;
pointer-events: none;
z-index: 5;
box-shadow: 0 0 5px rgba(255, 0, 0, 0.5);
}
#start-screen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 30, 0.9);
z-index: 30;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: white;
backdrop-filter: blur(5px);
}
#start-button {
margin-top: 30px;
padding: 15px 40px;
font-size: 24px;
background: linear-gradient(to bottom, #ff9900, #ff6600);
border: none;
border-radius: 50px;
color: white;
font-weight: bold;
cursor: pointer;
box-shadow: 0 0 20px rgba(255, 100, 0, 0.7);
transition: transform 0.2s, box-shadow 0.2s;
pointer-events: auto;
}
#start-button:active {
transform: scale(0.98);
}
#game-title-screen {
font-size: 48px;
margin-bottom: 20px;
text-align: center;
background: linear-gradient(to right, #ffcc00, #ff9900, #ff6600);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
text-shadow: 0 0 20px rgba(255, 100, 0, 0.7);
}
#instructions {
background: rgba(0, 0, 0, 0.6);
padding: 20px;
border-radius: 15px;
max-width: 90%;
text-align: center;
margin-top: 20px;
font-size: 18px;
line-height: 1.6;
backdrop-filter: blur(5px);
color: #fff;
}
#instructions h3 {
color: #ffcc00;
margin-bottom: 10px;
}
.controls-row {
display: flex;
justify-content: space-around;
width: 100%;
margin-top: 15px;
flex-wrap: wrap;
}
.control-item {
display: flex;
flex-direction: column;
align-items: center;
margin: 10px;
min-width: 120px;
color: #fff;
}
.control-icon {
width: 60px;
height: 60px;
background: rgba(255, 255, 255, 0.1);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 10px;
font-size: 24px;
backdrop-filter: blur(2px);
color: #fff;
}
.pulse {
animation: pulse 1s infinite alternate;
}
@keyframes pulse {
from { transform: scale(1); opacity: 0.7; }
to { transform: scale(1.1); opacity: 1; }
}
#game-stats {
display: flex;
justify-content: space-around;
width: 100%;
margin-top: 20px;
font-size: 20px;
background: rgba(0, 0, 0, 0.4);
padding: 10px;
border-radius: 10px;
backdrop-filter: blur(2px);
color: #fff;
}
.stat-item {
text-align: center;
color: #fff;
}
.stat-value {
font-weight: bold;
color: #ffcc00;
}
#difficulty-select {
margin-top: 20px;
padding: 10px 20px;
border-radius: 30px;
background: rgba(0, 0, 0, 0.5);
border: 1px solid #ff9900;
color: white;
font-size: 16px;
backdrop-filter: blur(2px);
width: 80%;
max-width: 300px;
}
#game-over-screen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.85);
z-index: 25;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: white;
display: none;
backdrop-filter: blur(10px);
}
#restart-button {
margin-top: 30px;
padding: 15px 40px;
font-size: 24px;
background: linear-gradient(to bottom, #00cc66, #009955);
border: none;
border-radius: 50px;
color: white;
font-weight: bold;
cursor: pointer;
box-shadow: 0 0 20px rgba(0, 200, 100, 0.7);
transition: transform 0.2s;
}
#restart-button:active {
transform: scale(0.98);
}
#final-score {
font-size: 36px;
margin: 20px 0;
color: #ffcc00;
}
/* 优化武器选择器 */
#weapon-selector {
position: absolute;
bottom: 140px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 15px;
z-index: 10;
pointer-events: auto;
background: rgba(0, 0, 0, 0.5);
padding: 10px;
border-radius: 15px;
backdrop-filter: blur(5px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.weapon-btn {
width: 80px;
height: 60px;
background: rgba(0, 0, 0, 0.5);
border: 2px solid #555;
border-radius: 10px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: white;
font-size: 14px;
cursor: pointer;
transition: all 0.2s;
backdrop-filter: blur(2px);
}
.weapon-btn.active {
border-color: #ff9900;
background: rgba(255, 153, 0, 0.2);
transform: scale(1.1);
}
.weapon-btn .icon {
font-size: 24px;
margin-bottom: 3px;
}
#mini-map {
position: absolute;
top: 20px;
right: 20px;
width: 150px;
height: 150px;
background: rgba(0, 0, 0, 0.5);
border: 2px solid #555;
border-radius: 10px;
z-index: 5;
overflow: hidden;
backdrop-filter: blur(2px);
}
#map-content {
position: relative;
width: 100%;
height: 100%;
}
.map-player {
position: absolute;
width: 8px;
height: 8px;
background: #3498db;
border-radius: 50%;
transform: translate(-50%, -50%);
z-index: 10;
}
.map-enemy {
position: absolute;
width: 6px;
height: 6px;
background: #e74c3c;
border-radius: 50%;
transform: translate(-50%, -50%);
z-index: 5;
}
.map-object {
position: absolute;
background: #8c7b6b;
transform: translate(-50%, -50%);
}
/* 优化能力按钮 */
#ability-bar {
position: absolute;
bottom: 200px;
right: 20px;
display: flex;
gap: 10px;
background: rgba(0, 0, 0, 0.5);
padding: 10px;
border-radius: 15px;
backdrop-filter: blur(5px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.ability-btn {
width: 70px;
height: 70px;
background: radial-gradient(circle, rgba(100,50,200,0.8) 0%, rgba(70,30,150,0.6) 70%);
border-radius: 50%;
border: 2px solid rgba(255, 255, 255, 0.8);
display: flex;
justify-content: center;
align-items: center;
font-size: 28px;
color: white;
pointer-events: auto;
box-shadow: 0 0 15px rgba(100, 50, 200, 0.6);
transition: transform 0.2s;
}
.ability-btn:active {
transform: scale(0.9);
}
.cooldown {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
color: white;
font-size: 14px;
font-weight: bold;
}
#mission-info {
position: absolute;
top: 70px;
left: 20px;
background: rgba(0, 0, 0, 0.5);
padding: 10px 15px;
border-radius: 10px;
font-size: 16px;
backdrop-filter: blur(2px);
color: #fff;
}
#level-display {
position: absolute;
top: 20px;
left: 250px;
background: rgba(0, 0, 0, 0.5);
padding: 5px 15px;
border-radius: 10px;
font-size: 20px;
font-weight: bold;
color: #ffcc00;
backdrop-filter: blur(2px);
}
.damage-indicator {
position: absolute;
background: rgba(255, 0, 0, 0.7);
border-radius: 50%;
pointer-events: none;
z-index: 5;
transform: translate(-50%, -50%);
}
#performance-stats {
position: absolute;
bottom: 10px;
left: 10px;
color: #0f0;
font-size: 12px;
background: rgba(0,0,0,0.5);
padding: 5px;
border-radius: 5px;
color: #fff;
}
.developer-info {
position: absolute;
bottom: 10px;
right: 10px;
font-size: 14px;
color: rgba(255, 255, 255, 0.6);
text-align: right;
padding: 5px 10px;
background: rgba(0, 0, 0, 0.4);
border-radius: 10px;
z-index: 100;
backdrop-filter: blur(2px);
}
.weapon-info {
position: absolute;
top: 120px;
left: 20px;
background: rgba(0, 0, 0, 0.5);
padding: 5px 10px;
border-radius: 10px;
font-size: 14px;
backdrop-filter: blur(2px);
color: #fff;
}
.upgrade-btn {
position: absolute;
bottom: 150px;
left: 20px;
width: 60px;
height: 60px;
background: radial-gradient(circle, rgba(50,200,50,0.8) 0%, rgba(20,150,20,0.6) 70%);
border-radius: 50%;
border: 2px solid rgba(255, 255, 255, 0.8);
display: flex;
justify-content: center;
align-items: center;
font-size: 24px;
color: white;
pointer-events: auto;
box-shadow: 0 0 15px rgba(50, 200, 50, 0.6);
transition: transform 0.2s;
z-index: 20;
}
.upgrade-btn:active {
transform: scale(0.9);
}
/* 触控引导 */
#touch-guide {
position: absolute;
bottom: 180px;
left: 50%;
transform: translateX(-50%);
text-align: center;
background: rgba(0, 0, 0, 0.6);
padding: 10px 20px;
border-radius: 20px;
font-size: 16px;
backdrop-filter: blur(5px);
border: 1px solid rgba(255, 255, 255, 0.3);
animation: fadeInOut 3s infinite;
z-index: 15;
color: #fff;
}
@keyframes fadeInOut {
0%, 100% { opacity: 0.7; }
50% { opacity: 1; }
}
/* 设置面板 */
#settings-panel {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 30, 0.9);
padding: 30px;
border-radius: 20px;
z-index: 40;
width: 90%;
max-width: 500px;
display: none;
backdrop-filter: blur(10px);
border: 2px solid #3498db;
}
.settings-row {
margin: 20px 0;
display: flex;
align-items: center;
justify-content: space-between;
}
.settings-label {
font-size: 18px;
color: #ffcc00;
margin-right: 20px;
}
.slider-container {
flex: 1;
max-width: 300px;
}
.slider-value {
min-width: 40px;
text-align: center;
color: #ffcc00;
font-weight: bold;
}
input[type="range"] {
width: 100%;
height: 10px;
border-radius: 5px;
background: #1a3a5f;
outline: none;
}
#settings-button {
position: absolute;
top: 20px;
right: 20px;
width: 50px;
height: 50px;
background: rgba(0, 0, 0, 0.5);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
font-size: 24px;
color: white;
z-index: 30;
pointer-events: auto;
backdrop-filter: blur(2px);
border: 1px solid #3498db;
}
#close-settings {
position: absolute;
top: 15px;
right: 15px;
background: transparent;
border: none;
color: #ffcc00;
font-size: 24px;
cursor: pointer;
}
#save-settings {
padding: 12px 30px;
background: linear-gradient(to bottom, #00cc66, #009955);
border: none;
border-radius: 50px;
color: white;
font-size: 18px;
font-weight: bold;
cursor: pointer;
margin-top: 20px;
width: 100%;
box-shadow: 0 0 15px rgba(0, 200, 100, 0.5);
}
#return-to-desktop {
padding: 12px 30px;
background: linear-gradient(to bottom, #ff9900, #ff6600);
border: none;
border-radius: 50px;
color: white;
font-size: 18px;
font-weight: bold;
cursor: pointer;
margin-top: 10px;
width: 100%;
box-shadow: 0 0 15px rgba(255, 100, 0, 0.5);
}
/* 地图选择面板 */
#map-select-panel {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 30, 0.9);
padding: 30px;
border-radius: 20px;
z-index: 35;
width: 90%;
max-width: 500px;
display: none;
backdrop-filter: blur(10px);
border: 2px solid #ff9900;
}
.map-option {
margin: 15px 0;
padding: 15px;
background: rgba(0, 0, 0, 0.5);
border-radius: 10px;
cursor: pointer;
transition: all 0.3s;
text-align: center;
border: 2px solid #3498db;
color: #fff;
}
.map-option:hover, .map-option.selected {
background: rgba(50, 100, 200, 0.3);
border-color: #ff9900;
transform: scale(1.05);
}
.map-option h3 {
color: #ffcc00;
margin-bottom: 10px;
}
.map-description {
font-size: 14px;
color: #ccc;
}
/* 模式选择按钮 */
.mode-selector {
display: flex;
justify-content: space-around;
margin: 20px 0;
}
.mode-btn {
padding: 10px 20px;
background: rgba(0, 0, 0, 0.5);
border: 2px solid #3498db;
border-radius: 30px;
color: white;
font-size: 16px;
cursor: pointer;
transition: all 0.3s;
}
.mode-btn.active {
background: rgba(50, 150, 255, 0.3);
border-color: #ff9900;
transform: scale(1.1);
}
/* 启动动画 */
#splash-screen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #0c1a2d, #1a3a5f);
z-index: 100;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: white;
}
#splash-logo {
font-size: 64px;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 5px;
margin-bottom: 20px;
text-align: center;
background: linear-gradient(to right, #ff9900, #ffcc00);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
text-shadow: 0 0 20px rgba(255, 100, 0, 0.7);
animation: logoPulse 2s infinite alternate;
}
@keyframes logoPulse {
0% { transform: scale(1); opacity: 0.8; }
100% { transform: scale(1.1); opacity: 1; }
}
#studio-name {
font-size: 24px;
margin-top: 20px;
opacity: 0;
animation: fadeIn 1.5s 1.5s forwards;
}
@keyframes fadeIn {
to { opacity: 1; }
}
.vehicle {
position: absolute;
z-index: 2;
}
@media (max-width: 768px) {
/* 移动端优化 */
#touch-joystick {
width: 120px;
height: 120px;
bottom: 20px;
left: 20px;
}
#joystick-knob {
width: 50px;
height: 50px;
}
#shoot-button {
width: 100px;
height: 100px;
bottom: 25px;
right: 25px;
font-size: 24px;
}
#reload-button {
width: 70px;
height: 70px;
bottom: 30px;
right: 140px;
font-size: 14px;
}
#game-title {
font-size: 28px;
}
.control-item {
min-width: 100px;
margin: 5px;
}
#mini-map {
width: 120px;
height: 120px;
}
#weapon-selector {
bottom: 120px;
gap: 8px;
}
.weapon-btn {
width: 65px;
height: 50px;
font-size: 12px;
}
.ability-btn {
width: 60px;
height: 60px;
font-size: 24px;
}
.upgrade-btn {
bottom: 120px;
left: 15px;
width: 50px;
height: 50px;
font-size: 20px;
}
.developer-info {
font-size: 12px;
bottom: 5px;
right: 5px;
}
#touch-guide {
bottom: 150px;
font-size: 14px;
padding: 8px 15px;
}
.settings-row {
flex-direction: column;
align-items: flex-start;
}
.settings-label {
margin-bottom: 10px;
}
.slider-container {
width: 100%;
}
}
</style>
</head>
<body>
<div id="splash-screen">
<div id="splash-logo">AREMM</div>
<div id="studio-name">鼠神工作室</div>
</div>
<div id="game-container">
<canvas id="game-canvas"></canvas>
<div id="ui-overlay">
<div id="game-title">AREMM</div>
<div id="level-display">关卡: 1</div>
<div id="health-bar">
<div id="health-fill"></div>
<div id="health-text">生命值: 100%</div>
</div>
<div id="ammo-display">弹药: 30/90</div>
<div id="score-display">分数: 0</div>
<div id="mission-info">任务: 消灭所有敌人 (0/8)</div>
<div id="performance-stats">FPS: 60</div>
<div class="weapon-info">当前武器: 手枪 (1级)</div>
<div id="mini-map">
<div id="map-content"></div>
</div>
<div id="touch-joystick">
<div id="joystick-knob"></div>
</div>
<div id="shoot-button">射击</div>
<div id="reload-button">装弹</div>
<div class="upgrade-btn" title="升级武器">⬆️</div>
<div id="weapon-selector">
<div class="weapon-btn active" data-weapon="pistol">
<div class="icon">🔫</div>
<div>手枪</div>
</div>
<div class="weapon-btn" data-weapon="shotgun">
<div class="icon">💥</div>
<div>霰弹枪</div>
</div>
<div class="weapon-btn" data-weapon="rifle">
<div class="icon">🔍</div>
<div>步枪</div>
</div>
<div class="weapon-btn" data-weapon="rocket">
<div class="icon">🚀</div>
<div>火箭筒</div>
</div>
</div>
<div id="ability-bar">
<div class="ability-btn" id="grenade-btn">💣</div>
<div class="ability-btn" id="heal-btn">❤️</div>
</div>
<div id="game-message">游戏开始!消灭所有敌人</div>
<div id="touch-guide">滑动右侧屏幕控制视角</div>
<div class="crosshair"></div>
<div id="settings-button">⚙️</div>
<div class="developer-info">开发者: 烛光深海独行<br>S20124005 快手号</div>
</div>
<div id="start-screen">
<div id="game-title-screen">AREMM</div>
<p>高级战术射击游戏</p>
<div id="game-stats">
<div class="stat-item">
<div>最高分数</div>
<div class="stat-value" id="high-score">0</div>
</div>
<div class="stat-item">
<div>消灭敌人</div>
<div class="stat-value" id="total-kills">0</div>
</div>
<div class="stat-item">
<div>最高关卡</div>
<div class="stat-value" id="max-level">1</div>
</div>
</div>
<div class="mode-selector">
<div class="mode-btn active" data-mode="campaign">战役模式</div>
<div class="mode-btn" data-mode="team">团队竞技</div>
</div>
<select id="difficulty-select">
<option value="easy">简单模式</option>
<option value="normal" selected>普通模式</option>
<option value="hard">困难模式</option>
<option value="extreme">极限模式</option>
</select>
<button id="map-select-button">选择地图</button>
<button id="start-button">开始任务</button>
<div id="instructions">
<h3>操作指南</h3>
<div class="controls-row">
<div class="control-item">
<div class="control-icon">←</div>
<div>移动摇杆</div>
</div>
<div class="control-item">
<div class="control-icon">↔</div>
<div>滑动屏幕控制视角</div>
</div>
<div class="control-item">
<div class="control-icon" style="background: rgba(255,50,50,0.3);">🔫</div>
<div>射击按钮</div>
</div>
<div class="control-item">
<div class="control-icon" style="background: rgba(50,100,255,0.3);">↻</div>
<div>装弹按钮</div>
</div>
</div>
</div>
<div class="developer-info" style="position: relative; margin-top: 20px;">
开发者: 烛光深海独行<br>S20124005 快手号
</div>
</div>
<div id="game-over-screen">
<h2 id="game-over-title">任务失败</h2>
<div id="final-score">分数: 0</div>
<div id="level-reached">关卡: 1</div>
<p>你被敌人击败了</p>
<button id="restart-button">重新开始</button>
<div class="developer-info" style="position: absolute; bottom: 20px;">
开发者: 烛光深海独行<br>S20124005 快手号
</div>
</div>
<!-- 设置面板 -->
<div id="settings-panel">
<button id="close-settings">×</button>
<h2 style="text-align: center; color: #ffcc00; margin-bottom: 30px;">游戏设置</h2>
<div class="settings-row">
<div class="settings-label">视角灵敏度:</div>
<div class="slider-container">
<input type="range" id="sensitivity-slider" min="1" max="20" value="10">
</div>
<div class="slider-value" id="sensitivity-value">10</div>
</div>
<div class="settings-row">
<div class="settings-label">移动速度:</div>
<div class="slider-container">
<input type="range" id="speed-slider" min="1" max="15" value="8">
</div>
<div class="slider-value" id="speed-value">8</div>
</div>
<div class="settings-row">
<div class="settings-label">摇杆模式:</div>
<div>
<select id="joystick-mode" style="background: rgba(0,0,0,0.5); color: white; padding: 8px 15px; border-radius: 20px; border: 1px solid #3498db;">
<option value="fixed">固定摇杆</option>
<option value="floating">浮动摇杆</option>
</select>
</div>
</div>
<div class="settings-row">
<div class="settings-label">碰撞检测:</div>
<div>
<select id="collision-mode" style="background: rgba(0,0,0,0.5); color: white; padding: 8px 15px; border-radius: 20px; border: 1px solid #3498db;">
<option value="normal">正常模式</option>
<option value="precise">精确模式</option>
</select>
</div>
</div>
<button id="save-settings">保存设置</button>
<button id="return-to-desktop">返回桌面</button>
</div>
<!-- 地图选择面板 -->
<div id="map-select-panel">
<button id="close-map-select">×</button>
<h2 style="text-align: center; color: #ffcc00; margin-bottom: 30px;">选择地图</h2>
<div class="map-option selected" data-map="city">
<h3>城市战场</h3>
<div class="map-description">现代都市环境,高楼林立,适合巷战</div>
</div>
<div class="map-option" data-map="desert">
<h3>沙漠废墟</h3>
<div class="map-description">开阔沙漠地带,视野开阔,适合狙击</div>
</div>
<div class="map-option" data-map="jungle">
<h3>丛林基地</h3>
<div class="map-description">茂密丛林环境,隐蔽性强,适合伏击</div>
</div>
<button id="confirm-map">确认选择</button>
</div>
</div>
<script>
// 游戏主变量
let scene, camera, renderer;
let moveForward = false, moveBackward = false, moveLeft = false, moveRight = false;
let velocity = new THREE.Vector3();
let direction = new THREE.Vector3();
let clock = new THREE.Clock();
let objects = [];
let walls = [];
let player, enemies = [], teammates = [];
let enemyHitBoxes = []; // 用于射线检测的敌人Mesh数组
let joystickActive = false;
let joystickPosition = new THREE.Vector2();
let touchStartPosition = new THREE.Vector2();
let playerHealth = 100;
let playerScore = 0;
let ammoCount = 30;
let totalAmmo = 90;
let isReloading = false;
let gameActive = false;
let enemySpawnTimer = 0;
let particles = [];
let difficulty = 'normal';
let highScore = 0;
let totalKills = 0;
let maxLevel = 1;
let currentLevel = 1;
let currentWeapon = 'pistol';
let grenadeCooldown = 0;
let healCooldown = 0;
let missionEnemies = 0;
let enemiesKilled = 0;
let lastFrameTime = performance.now();
let fps = 60;
let damageIndicators = [];
let weaponUpgrades = {
pistol: { level: 1, damageMultiplier: 1 },
shotgun: { level: 1, damageMultiplier: 1 },
rifle: { level: 1, damageMultiplier: 1 },
rocket: { level: 1, damageMultiplier: 1 }
};
// 游戏模式
let gameMode = 'campaign'; // 'campaign' 或 'team'
// 当前地图
let currentMap = 'city';
// 碰撞检测模式
let collisionMode = 'normal';
let collisionBoxes = [];
// 游戏设置
let gameSettings = {
sensitivity: 0.001,
moveSpeed: 0.08,
joystickMode: 'fixed',
collisionMode: 'normal'
};
// 武器属性
const weapons = {
pistol: {
damage: 25,
fireRate: 0.5,
ammoCost: 1,
range: 100,
name: "手枪",
model: "box"
},
shotgun: {
damage: 40,
fireRate: 1.0,
ammoCost: 2,
range: 60,
name: "霰弹枪",
model: "cylinder"
},
rifle: {
damage: 30,
fireRate: 0.2,
ammoCost: 1,
range: 150,
name: "步枪",
model: "box"
},
rocket: {
damage: 100,
fireRate: 1.5,
ammoCost: 5,
range: 200,
name: "火箭筒",
model: "cylinder"
}
};
// 敌人类型
const enemyTypes = {
soldier: { health: 100, damage: 5, speed: 0.05, color: 0xe74c3c },
heavy: { health: 200, damage: 8, speed: 0.03, color: 0x9b59b6 },
sniper: { health: 80, damage: 15, speed: 0.04, color: 0x3498db },
elite: { health: 150, damage: 12, speed: 0.06, color: 0xf1c40f }
};
// 队友类型
const teammateTypes = {
soldier: { health: 120, damage: 4, speed: 0.04, color: 0x3498db },
medic: { health: 100, damage: 3, speed: 0.05, color: 0x2ecc71 },
sniper: { health: 80, damage: 10, speed: 0.03, color: 0x9b59b6 }
};
// 地图配置 - 增加亮度
const mapConfigs = {
city: {
groundColor: 0x8c8c8c,
obstacleColor: 0xa59b8b,
wallColor: 0xc5c5c5,
ambientLight: 0x808080,
fogColor: 0x2a4b60,
fogNear: 0,
fogFar: 500
},
desert: {
groundColor: 0xe2c48c,
obstacleColor: 0xdd953f,
wallColor: 0xb0723d,
ambientLight: 0xa08050,
fogColor: 0xe2b679,
fogNear: 50,
fogFar: 400
},
jungle: {
groundColor: 0x5d8b55,
obstacleColor: 0x758b3f,
wallColor: 0xafccaf,
ambientLight: 0x608060,
fogColor: 0x3d5d3d,
fogNear: 10,
fogFar: 300
}
};
// 难度系数
const difficultyFactors = {
easy: { health: 0.8, damage: 0.8, speed: 0.9 },
normal: { health: 1.0, damage: 1.0, speed: 1.0 },
hard: { health: 1.2, damage: 1.2, speed: 1.1 },
extreme: { health: 1.5, damage: 1.5, speed: 1.2 }
};
// 初始化游戏
function init() {
// 显示启动动画
setTimeout(() => {
document.getElementById('splash-screen').style.display = 'none';
document.getElementById('start-screen').style.display = 'flex';
}, 3000);
// 加载游戏数据
loadGameData();
loadSettings();
// 创建场景
createScene();
// 创建相机
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.y = 10;
// 创建渲染器
renderer = new THREE.WebGLRenderer({
canvas: document.getElementById('game-canvas'),
antialias: true,
alpha: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
// 添加光源
const ambientLight = new THREE.AmbientLight(mapConfigs[currentMap].ambientLight, 0.8);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1.0);
directionalLight.position.set(1, 1, 0.5).normalize();
directionalLight.castShadow = true;
directionalLight.shadow.mapSize.width = 1024;
directionalLight.shadow.mapSize.height = 1024;
directionalLight.shadow.camera.near = 0.5;
directionalLight.shadow.camera.far = 500;
scene.add(directionalLight);
// 创建地面
createGround();
// 添加障碍物
addObstacles();
// 添加载具
addVehicles();
// 创建玩家
createPlayer();
// 设置触摸控制
setupTouchControls();
// 设置事件监听
setupEventListeners();
// 开始游戏循环
animate();
// 窗口大小调整
window.addEventListener('resize', onWindowResize);
// 键盘事件
window.addEventListener('keydown', onKeyDown);
window.addEventListener('keyup', onKeyUp);
}
// 创建场景
function createScene() {
scene = new THREE.Scene();
scene.background = new THREE.Color(mapConfigs[currentMap].fogColor);
scene.fog = new THREE.Fog(mapConfigs[currentMap].fogColor, mapConfigs[currentMap].fogNear, mapConfigs[currentMap].fogFar);
}
// 创建地面
function createGround() {
const groundGeometry = new THREE.PlaneGeometry(1000, 1000);
const groundMaterial = new THREE.MeshPhongMaterial({
color: mapConfigs[currentMap].groundColor,
shininess: 30
});
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.rotation.x = -Math.PI / 2;
ground.receiveShadow = true;
scene.add(ground);
}
// 添加载具
function addVehicles() {
// 创建吉普车
for (let i = 0; i < 4; i++) {
const jeep = createJeep();
jeep.position.set(
Math.random() * 180 - 90,
1,
Math.random() * 180 - 90
);
jeep.rotation.y = Math.random() * Math.PI;
scene.add(jeep);
objects.push(jeep);
walls.push(jeep);
}
// 创建坦克
if (currentMap === 'desert') {
for (let i = 0; i < 2; i++) {
const tank = createTank();
tank.position.set(
Math.random() * 180 - 90,
1.5,
Math.random() * 180 - 90
);
tank.rotation.y = Math.random() * Math.PI;
scene.add(tank);
objects.push(tank);
walls.push(tank);
}
}
}
// 创建吉普车模型
function createJeep() {
const jeep = new THREE.Group();
// 车身
const bodyGeometry = new THREE.BoxGeometry(4, 1.5, 2);
const bodyMaterial = new THREE.MeshPhongMaterial({ color: 0x2c3e50 });
const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
body.position.y = 1;
body.castShadow = true;
jeep.add(body);
// 车顶
const roofGeometry = new THREE.BoxGeometry(2, 0.5, 1.8);
const roofMaterial = new THREE.MeshPhongMaterial({ color: 0x34495e });
const roof = new THREE.Mesh(roofGeometry, roofMaterial);
roof.position.set(0, 1.75, 0);
roof.castShadow = true;
jeep.add(roof);
// 车轮
const wheelGeometry = new THREE.CylinderGeometry(0.4, 0.4, 0.2, 16);
wheelGeometry.rotateZ(Math.PI/2);
const wheelMaterial = new THREE.MeshPhongMaterial({ color: 0x111111 });
// 前轮
const wheelFL = new THREE.Mesh(wheelGeometry, wheelMaterial);
wheelFL.position.set(1.5, 0.8, 1);
wheelFL.castShadow = true;
jeep.add(wheelFL);
const wheelFR = new THREE.Mesh(wheelGeometry, wheelMaterial);
wheelFR.position.set(1.5, 0.8, -1);
wheelFR.castShadow = true;
jeep.add(wheelFR);
// 后轮
const wheelRL = new THREE.Mesh(wheelGeometry, wheelMaterial);
wheelRL.position.set(-1.5, 0.8, 1);
wheelRL.castShadow = true;
jeep.add(wheelRL);
const wheelRR = new THREE.Mesh(wheelGeometry, wheelMaterial);
wheelRR.position.set(-1.5, 0.8, -1);
wheelRR.castShadow = true;
jeep.add(wheelRR);
return jeep;
}
// 创建坦克模型
function createTank() {
const tank = new THREE.Group();
// 坦克主体
const bodyGeometry = new THREE.BoxGeometry(4, 1.5, 3);
const bodyMaterial = new THREE.MeshPhongMaterial({ color: 0x7f8c8d });
const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
body.position.y = 1;
body.castShadow = true;
tank.add(body);
// 炮塔
const turretGeometry = new THREE.CylinderGeometry(1, 1, 0.8, 16);
const turretMaterial = new THREE.MeshPhongMaterial({ color: 0x95a5a6 });
const turret = new THREE.Mesh(turretGeometry, turretMaterial);
turret.position.y = 2.2;
turret.castShadow = true;
tank.add(turret);
// 炮管
const barrelGeometry = new THREE.CylinderGeometry(0.2, 0.2, 3, 8);
barrelGeometry.rotateZ(Math.PI/2);
const barrelMaterial = new THREE.MeshPhongMaterial({ color: 0x555555 });
const barrel = new THREE.Mesh(barrelGeometry, barrelMaterial);
barrel.position.set(1.5, 2.2, 0);
barrel.castShadow = true;
tank.add(barrel);
// 车轮
const wheelGeometry = new THREE.CylinderGeometry(0.5, 0.5, 0.3, 16);
wheelGeometry.rotateZ(Math.PI/2);
const wheelMaterial = new THREE.MeshPhongMaterial({ color: 0x111111 });
for (let i = 0; i < 6; i++) {
const wheel = new THREE.Mesh(wheelGeometry, wheelMaterial);
wheel.position.set(-1.5 + i*0.6, 0.8, 1.5);
wheel.castShadow = true;
tank.add(wheel);
const wheel2 = new THREE.Mesh(wheelGeometry, wheelMaterial);
wheel2.position.set(-1.5 + i*0.6, 0.8, -1.5);
wheel2.castShadow = true;
tank.add(wheel2);
}
return tank;
}
// 加载游戏数据
function loadGameData() {
const savedHighScore = localStorage.getItem('highScore');
const savedTotalKills = localStorage.getItem('totalKills');
const savedMaxLevel = localStorage.getItem('maxLevel');
highScore = savedHighScore ? parseInt(savedHighScore) : 0;
totalKills = savedTotalKills ? parseInt(savedTotalKills) : 0;
maxLevel = savedMaxLevel ? parseInt(savedMaxLevel) : 1;
document.getElementById('high-score').textContent = highScore;
document.getElementById('total-kills').textContent = totalKills;
document.getElementById('max-level').textContent = maxLevel;
}
// 加载设置
function loadSettings() {
const savedSettings = localStorage.getItem('gameSettings');
if (savedSettings) {
gameSettings = JSON.parse(savedSettings);
document.getElementById('sensitivity-slider').value = gameSettings.sensitivity * 1000;
document.getElementById('sensitivity-value').textContent = Math.round(gameSettings.sensitivity * 1000);
document.getElementById('speed-slider').value = gameSettings.moveSpeed * 100;
document.getElementById('speed-value').textContent = Math.round(gameSettings.moveSpeed * 100);
document.getElementById('joystick-mode').value = gameSettings.joystickMode;
document.getElementById('collision-mode').value = gameSettings.collisionMode;
}
}
// 保存设置
function saveSettings() {
localStorage.setItem('gameSettings', JSON.stringify(gameSettings));
}
// 保存游戏数据
function saveGameData() {
localStorage.setItem('highScore', highScore);
localStorage.setItem('totalKills', totalKills);
localStorage.setItem('maxLevel', maxLevel);
}
// 开始游戏
function startGame() {
document.getElementById('start-screen').style.display = 'none';
document.getElementById('game-over-screen').style.display = 'none';
gameActive = true;
currentLevel = 1;
// 获取难度设置
difficulty = document.getElementById('difficulty-select').value;
// 获取游戏模式
gameMode = document.querySelector('.mode-btn.active').dataset.mode;
// 重置游戏状态
resetGameState();
// 创建关卡
setupLevel();
// 显示操作提示
setTimeout(() => {
document.getElementById('touch-guide').style.display = 'none';
}, 5000);
}
// 重置游戏状态
function resetGameState() {
playerHealth = 100;
playerScore = 0;
ammoCount = 30;
totalAmmo = 90;
enemiesKilled = 0;
enemyHitBoxes = []; // 清空敌人检测数组
particles = []; // 清空粒子
damageIndicators = []; // 清空伤害指示器
// 移除现有敌人和队友
enemies.forEach(enemy => {
scene.remove(enemy.mesh);
const hpBar = document.getElementById(`enemy-hp-${enemy.id}`);
if (hpBar) hpBar.remove();
});
enemies = [];
teammates.forEach(teammate => {
scene.remove(teammate.mesh);
const hpBar = document.getElementById(`teammate-hp-${teammate.id}`);
if (hpBar) hpBar.remove();
});
teammates = [];
// 重置玩家位置
player.mesh.position.set(0, 10, 0);
player.mesh.rotation.set(0, 0, 0);
camera.rotation.set(0, 0, 0);
// 重置移动状态
moveForward = moveBackward = moveLeft = moveRight = false;
// 重置武器模型
createWeaponModel(currentWeapon);
// 重置UI
updateHealthBar();
updateAmmoDisplay();
updateScore();
updateWeaponInfo();
}
// 设置关卡
function setupLevel() {
document.getElementById('level-display').textContent = `关卡: ${currentLevel}`;
// 根据关卡设置敌人数量
let baseEnemies;
if (gameMode === 'campaign') {
baseEnemies = 4 + currentLevel * 2;
missionEnemies = Math.min(baseEnemies, 20);
document.getElementById('mission-info').textContent = `任务: 消灭所有敌人 (0/${missionEnemies})`;
} else {
missionEnemies = 4;
document.getElementById('mission-info').textContent = `团队竞技: 消灭敌方队伍`;
}
// 创建敌人
createEnemies(missionEnemies);
// 团队竞技模式下创建队友
if (gameMode === 'team') {
createTeammates(3);
}
// 显示游戏开始消息
let message = gameMode === 'campaign'
? `第 ${currentLevel} 关开始!消灭所有敌人`
: "团队竞技开始!与队友并肩作战";
showMessage(message, 3000);
}
// 游戏结束
function gameOver() {
gameActive = false;
document.getElementById('game-over-screen').style.display = 'flex';
document.getElementById('final-score').textContent = `分数: ${playerScore}`;
document.getElementById('level-reached').textContent = `关卡: ${currentLevel}`;
// 更新最高分和最高关卡
if (playerScore > highScore) {
highScore = playerScore;
document.getElementById('high-score').textContent = highScore;
}
if (currentLevel > maxLevel) {
maxLevel = currentLevel;
document.getElementById('max-level').textContent = maxLevel;
}
// 保存游戏数据
saveGameData();
}
// 重新开始游戏
function restartGame() {
document.getElementById('game-over-screen').style.display = 'none';
startGame();
}
// 添加障碍物
function addObstacles() {
// 清除现有障碍物
objects.forEach(obj => scene.remove(obj));
objects = [];
walls = [];
// 创建随机障碍物
const obstacleGeometry = new THREE.BoxGeometry(10, 10, 10);
const obstacleMaterial = new THREE.MeshPhongMaterial({
color: mapConfigs[currentMap].obstacleColor,
shininess: 50
});
// 添加大型障碍物
for (let i = 0; i < 10; i++) {
const obstacle = new THREE.Mesh(obstacleGeometry, obstacleMaterial);
obstacle.position.x = Math.random() * 200 - 100;
obstacle.position.z = Math.random() * 200 - 100;
obstacle.position.y = 5;
obstacle.castShadow = true;
obstacle.receiveShadow = true;
scene.add(obstacle);
objects.push(obstacle);
// 添加碰撞盒
if (gameSettings.collisionMode === 'precise') {
const boxHelper = new THREE.BoxHelper(obstacle, 0x00ff00);
scene.add(boxHelper);
collisionBoxes.push({
object: obstacle,
helper: boxHelper
});
}
}
// 添加随机箱子
for (let i = 0; i < 15; i++) {
const boxGeometry = new THREE.BoxGeometry(4, 4, 4);
const boxMaterial = new THREE.MeshPhongMaterial({
color: Math.random() > 0.5 ? 0xd35400 : 0x7f8c8d
});
const box = new THREE.Mesh(boxGeometry, boxMaterial);
box.position.set(
Math.random() * 200 - 100,
2,
Math.random() * 200 - 100
);
box.castShadow = true;
box.receiveShadow = true;
scene.add(box);
objects.push(box);
// 添加碰撞盒
if (gameSettings.collisionMode === 'precise') {
const boxHelper = new THREE.BoxHelper(box, 0x00ff00);
scene.add(boxHelper);
collisionBoxes.push({
object: box,
helper: boxHelper
});
}
}
// 添加墙体
for (let i = 0; i < 6; i++) {
const wallLength = 20 + Math.random() * 20;
const wallGeometry = new THREE.BoxGeometry(wallLength, 10, 2);
const wallMaterial = new THREE.MeshPhongMaterial({
color: mapConfigs[currentMap].wallColor
});
const wall = new THREE.Mesh(wallGeometry, wallMaterial);
wall.position.set(
Math.random() * 180 - 90,
5,
Math.random() * 180 - 90
);
wall.rotation.y = Math.random() * Math.PI;
wall.castShadow = true;
wall.receiveShadow = true;
scene.add(wall);
objects.push(wall);
walls.push(wall); // 添加到墙体数组
// 添加碰撞盒
if (gameSettings.collisionMode === 'precise') {
const boxHelper = new THREE.BoxHelper(wall, 0x00ff00);
scene.add(boxHelper);
collisionBoxes.push({
object: wall,
helper: boxHelper
});
}
}
// 为城市地图添加额外建筑
if (currentMap === 'city') {
for (let i = 0; i < 4; i++) {
const buildingHeight = 30 + Math.random() * 30;
const buildingGeometry = new THREE.BoxGeometry(15, buildingHeight, 15);
const buildingMaterial = new THREE.MeshPhongMaterial({
color: 0x708090,
shininess: 40
});
const building = new THREE.Mesh(buildingGeometry, buildingMaterial);
building.position.set(
Math.random() * 180 - 90,
buildingHeight/2,
Math.random() * 180 - 90
);
building.castShadow = true;
scene.add(building);
objects.push(building);
walls.push(building); // 添加到墙体数组
}
}
// 为沙漠地图添加岩石
if (currentMap === 'desert') {
for (let i = 0; i < 8; i++) {
const rockSize = 3 + Math.random() * 4;
const rockGeometry = new THREE.BoxGeometry(rockSize, rockSize, rockSize);
const rockMaterial = new THREE.MeshPhongMaterial({
color: 0x8B4513,
shininess: 30
});
const rock = new THREE.Mesh(rockGeometry, rockMaterial);
rock.position.set(
Math.random() * 180 - 90,
rockSize/2,
Math.random() * 180 - 90
);
rock.rotation.set(
Math.random() * Math.PI,
Math.random() * Math.PI,
Math.random() * Math.PI
);
rock.castShadow = true;
scene.add(rock);
objects.push(rock);
}
}
// 为丛林地图添加树木
if (currentMap === 'jungle') {
for (let i = 0; i < 15; i++) {
const treeHeight = 8 + Math.random() * 6;
const trunkGeometry = new THREE.CylinderGeometry(0.5, 0.7, treeHeight, 8);
const trunkMaterial = new THREE.MeshPhongMaterial({
color: 0x8B4513,
shininess: 20
});
const trunk = new THREE.Mesh(trunkGeometry, trunkMaterial);
const canopyGeometry = new THREE.SphereGeometry(3, 8, 8);
const canopyMaterial = new THREE.MeshPhongMaterial({
color: 0x228833,
shininess: 10
});
const canopy = new THREE.Mesh(canopyGeometry, canopyMaterial);
canopy.position.y = treeHeight/2 + 2;
const tree = new THREE.Group();
tree.add(trunk);
tree.add(canopy);
tree.position.set(
Math.random() * 180 - 90,
0,
Math.random() * 180 - 90
);
scene.add(tree);
objects.push(tree);
walls.push(tree); // 添加到墙体数组
}
}
}
// 创建玩家(优化模型)
function createPlayer() {
player = {
mesh: new THREE.Object3D(),
speed: gameSettings.moveSpeed,
rotationSpeed: 0.03,
weaponMesh: null,
collisionBox: new THREE.Box3()
};
// 玩家身体(更精细)
const torsoGeometry = new THREE.BoxGeometry(1.2, 1.5, 0.8);
const torsoMaterial = new THREE.MeshPhongMaterial({
color: 0x3498db,
shininess: 60
});
const torso = new THREE.Mesh(torsoGeometry, torsoMaterial);
torso.position.y = 1.2;
torso.castShadow = true;
player.mesh.add(torso);
// 玩家头部
const headGeometry = new THREE.SphereGeometry(0.7, 16, 16);
const headMaterial = new THREE.MeshPhongMaterial({
color: 0xffcc99,
shininess: 50
});
const head = new THREE.Mesh(headGeometry, headMaterial);
head.position.y = 2.5;
head.castShadow = true;
player.mesh.add(head);
// 玩家手臂
const armGeometry = new THREE.BoxGeometry(0.4, 1.2, 0.4);
const armMaterial = new THREE.MeshPhongMaterial({
color: 0x3498db,
shininess: 60
});
// 左臂
const leftArm = new THREE.Mesh(armGeometry, armMaterial);
leftArm.position.set(-0.8, 1.2, 0);
leftArm.castShadow = true;
player.mesh.add(leftArm);
// 右臂
const rightArm = new THREE.Mesh(armGeometry, armMaterial);
rightArm.position.set(0.8, 1.2, 0);
rightArm.castShadow = true;
player.mesh.add(rightArm);
// 玩家腿部
const legGeometry = new THREE.BoxGeometry(0.5, 1.2, 0.5);
const legMaterial = new THREE.MeshPhongMaterial({
color: 0x2c3e50,
shininess: 60
});
// 左腿
const leftLeg = new THREE.Mesh(legGeometry, legMaterial);
leftLeg.position.set(-0.4, 0, 0);
leftLeg.castShadow = true;
player.mesh.add(leftLeg);
// 右腿
const rightLeg = new THREE.Mesh(legGeometry, legMaterial);
rightLeg.position.set(0.4, 0, 0);
rightLeg.castShadow = true;
player.mesh.add(rightLeg);
// 创建初始武器
createWeaponModel('pistol');
scene.add(player.mesh);
player.mesh.position.y = 10;
player.mesh.add(camera);
// 修复:调整相机位置,避免看到玩家身体
camera.position.set(0, 1.8, 0.5);
// 修复:缩小玩家模型
player.mesh.scale.set(0.8, 0.8, 0.8);
// 设置玩家碰撞盒
player.collisionBox.setFromObject(player.mesh);
}
// 创建队友(优化模型)
function createTeammates(count) {
for (let i = 0; i < count; i++) {
const teammateId = Date.now() + i + 1000; // 确保ID不冲突
// 随机队友类型
const types = Object.keys(teammateTypes);
const type = types[Math.floor(Math.random() * types.length)];
const typeData = teammateTypes[type];
// 加强队友属性
const damage = typeData.damage * 1.4; // 伤害提高40%
const speed = typeData.speed * 1.2; // 速度提高20%
const teammate = {
mesh: new THREE.Object3D(),
health: typeData.health,
damage: damage,
speed: speed,
type: type,
attackCooldown: 0,
id: teammateId,
state: 'follow',
followDistance: 5 + i * 2,
lastStateChange: 0
};
// 队友身体
const torsoGeometry = new THREE.BoxGeometry(1.2, 1.5, 0.8);
const torsoMaterial = new THREE.MeshPhongMaterial({
color: typeData.color,
shininess: 50
});
const torso = new THREE.Mesh(torsoGeometry, torsoMaterial);
torso.position.y = 1.2;
torso.castShadow = true;
torso.userData = { teammateId: teammateId };
teammate.mesh.add(torso);
// 队友头部
const headGeometry = new THREE.SphereGeometry(0.7, 16, 16);
const headMaterial = new THREE.MeshPhongMaterial({
color: 0xffcc99,
shininess: 50
});
const head = new THREE.Mesh(headGeometry, headMaterial);
head.position.y = 2.5;
head.castShadow = true;
head.userData = { teammateId: teammateId };
teammate.mesh.add(head);
// 队友手臂
const armGeometry = new THREE.BoxGeometry(0.4, 1.2, 0.4);
const armMaterial = new THREE.MeshPhongMaterial({
color: typeData.color,
shininess: 50
});
// 左臂
const leftArm = new THREE.Mesh(armGeometry, armMaterial);
leftArm.position.set(-0.8, 1.2, 0);
leftArm.castShadow = true;
leftArm.userData = { teammateId: teammateId };
teammate.mesh.add(leftArm);
// 右臂
const rightArm = new THREE.Mesh(armGeometry, armMaterial);
rightArm.position.set(0.8, 1.2, 0);
rightArm.castShadow = true;
rightArm.userData = { teammateId: teammateId };
teammate.mesh.add(rightArm);
// 队友腿部
const legGeometry = new THREE.BoxGeometry(0.5, 1.2, 0.5);
const legMaterial = new THREE.MeshPhongMaterial({
color: 0x2c3e50,
shininess: 50
});
// 左腿
const leftLeg = new THREE.Mesh(legGeometry, legMaterial);
leftLeg.position.set(-0.4, 0, 0);
leftLeg.castShadow = true;
leftLeg.userData = { teammateId: teammateId };
teammate.mesh.add(leftLeg);
// 右腿
const rightLeg = new THREE.Mesh(legGeometry, legMaterial);
rightLeg.position.set(0.4, 0, 0);
rightLeg.castShadow = true;
rightLeg.userData = { teammateId: teammateId };
teammate.mesh.add(rightLeg);
// 队友武器
const weaponGeometry = new THREE.BoxGeometry(1.5, 0.3, 0.3);
const weaponMaterial = new THREE.MeshPhongMaterial({
color: 0x222222,
shininess: 80
});
const weapon = new THREE.Mesh(weaponGeometry, weaponMaterial);
weapon.position.set(1, 2.5, 0);
weapon.castShadow = true;
weapon.userData = { teammateId: teammateId };
teammate.mesh.add(weapon);
// 初始位置(在玩家周围)
const angle = (i / count) * Math.PI * 2;
const radius = 10;
teammate.mesh.position.set(
Math.cos(angle) * radius,
1.5,
Math.sin(angle) * radius
);
scene.add(teammate.mesh);
teammates.push(teammate);
// 创建队友血条UI
const hpBar = document.createElement('div');
hpBar.className = 'enemy-hp';
hpBar.id = `teammate-hp-${teammateId}`;
hpBar.style.background = 'linear-gradient(90deg, #00cc66, #00ff99)';
document.getElementById('ui-overlay').appendChild(hpBar);
}
}
// 创建武器模型
function createWeaponModel(weaponType) {
// 移除旧武器
if (player.weaponMesh) {
camera.remove(player.weaponMesh);
}
let weaponGeometry, weaponMaterial;
switch(weaponType) {
case 'pistol':
weaponGeometry = new THREE.BoxGeometry(1.5, 0.3, 0.3);
weaponMaterial = new THREE.MeshPhongMaterial({
color: 0x444444,
shininess: 80
});
break;
case 'shotgun':
weaponGeometry = new THREE.CylinderGeometry(0.2, 0.2, 2.5, 8);
weaponMaterial = new THREE.MeshPhongMaterial({
color: 0x8B4513,
shininess: 60
});
break;
case 'rifle':
weaponGeometry = new THREE.BoxGeometry(2.5, 0.4, 0.4);
weaponMaterial = new THREE.MeshPhongMaterial({
color: 0x2F4F4F,
shininess: 90
});
break;
case 'rocket':
weaponGeometry = new THREE.CylinderGeometry(0.3, 0.15, 3, 16);
weaponMaterial = new THREE.MeshPhongMaterial({
color: 0x808080,
shininess: 70
});
break;
default:
weaponGeometry = new THREE.BoxGeometry(1.5, 0.3, 0.3);
weaponMaterial = new THREE.MeshPhongMaterial({
color: 0x444444,
shininess: 80
});
}
const weapon = new THREE.Mesh(weaponGeometry, weaponMaterial);
// 根据武器类型调整位置(放在相机前方)
weapon.position.set(0.8, -0.4, -1.2);
weapon.rotation.x = Math.PI / 8;
// 将武器添加到相机上,而不是玩家模型上
camera.add(weapon);
player.weaponMesh = weapon;
}
// 创建敌人(优化模型)
function createEnemies(count) {
const diffFactor = difficultyFactors[difficulty];
for (let i = 0; i < count; i++) {
const enemyId = Date.now() + i;
// 根据关卡和难度决定敌人类型
let enemyType;
const rand = Math.random();
if (currentLevel > 5 && rand < 0.2) {
enemyType = 'elite';
} else if (currentLevel > 3 && rand < 0.3) {
enemyType = 'heavy';
} else if (currentLevel > 2 && rand < 0.5) {
enemyType = 'sniper';
} else {
enemyType = 'soldier';
}
const typeData = enemyTypes[enemyType];
// 应用难度系数
const health = typeData.health * diffFactor.health;
const damage = typeData.damage * diffFactor.damage;
const speed = typeData.speed * diffFactor.speed;
const enemy = {
mesh: new THREE.Object3D(),
health: health,
damage: damage,
speed: speed,
type: enemyType,
attackCooldown: 0,
id: enemyId,
state: 'patrol',
patrolTarget: new THREE.Vector3(
Math.random() * 200 - 100,
1.5,
Math.random() * 200 - 100
),
lastStateChange: 0
};
// 敌人身体
const torsoGeometry = new THREE.BoxGeometry(1.2, 1.5, 0.8);
const torsoMaterial = new THREE.MeshPhongMaterial({
color: typeData.color,
shininess: 50
});
const torso = new THREE.Mesh(torsoGeometry, torsoMaterial);
torso.position.y = 1.2;
torso.castShadow = true;
torso.userData = { enemyId: enemyId };
enemy.mesh.add(torso);
// 敌人头部
const headGeometry = new THREE.SphereGeometry(0.7, 16, 16);
const headMaterial = new THREE.MeshPhongMaterial({
color: 0xff9999,
shininess: 50
});
const head = new THREE.Mesh(headGeometry, headMaterial);
head.position.y = 2.5;
head.castShadow = true;
head.userData = { enemyId: enemyId };
enemy.mesh.add(head);
// 敌人手臂
const armGeometry = new THREE.BoxGeometry(0.4, 1.2, 0.4);
const armMaterial = new THREE.MeshPhongMaterial({
color: typeData.color,
shininess: 50
});
// 左臂
const leftArm = new THREE.Mesh(armGeometry, armMaterial);
leftArm.position.set(-0.8, 1.2, 0);
leftArm.castShadow = true;
leftArm.userData = { enemyId: enemyId };
enemy.mesh.add(leftArm);
// 右臂
const rightArm = new THREE.Mesh(armGeometry, armMaterial);
rightArm.position.set(0.8, 1.2, 0);
rightArm.castShadow = true;
rightArm.userData = { enemyId: enemyId };
enemy.mesh.add(rightArm);
// 敌人腿部
const legGeometry = new THREE.BoxGeometry(0.5, 1.2, 0.5);
const legMaterial = new THREE.MeshPhongMaterial({
color: 0x2c3e50,
shininess: 50
});
// 左腿
const leftLeg = new THREE.Mesh(legGeometry, legMaterial);
leftLeg.position.set(-0.4, 0, 0);
leftLeg.castShadow = true;
leftLeg.userData = { enemyId: enemyId };
enemy.mesh.add(leftLeg);
// 右腿
const rightLeg = new THREE.Mesh(legGeometry, legMaterial);
rightLeg.position.set(0.4, 0, 0);
rightLeg.castShadow = true;
rightLeg.userData = { enemyId: enemyId };
enemy.mesh.add(rightLeg);
// 敌人武器
const weaponGeometry = new THREE.BoxGeometry(1.5, 0.3, 0.3);
const weaponMaterial = new THREE.MeshPhongMaterial({
color: 0x222222,
shininess: 80
});
const weapon = new THREE.Mesh(weaponGeometry, weaponMaterial);
weapon.position.set(1, 2.5, 0);
weapon.castShadow = true;
weapon.userData = { enemyId: enemyId };
enemy.mesh.add(weapon);
// 随机位置
let validPosition = false;
let attempts = 0;
while (!validPosition && attempts < 15) {
enemy.mesh.position.x = Math.random() * 180 - 90;
enemy.mesh.position.z = Math.random() * 180 - 90;
enemy.mesh.position.y = 1.5;
// 确保不会在玩家附近生成
if (player.mesh.position.distanceTo(enemy.mesh.position) > 30) {
validPosition = true;
}
attempts++;
}
scene.add(enemy.mesh);
enemies.push(enemy);
// 将敌人的Mesh添加到检测数组
enemy.mesh.traverse(child => {
if (child instanceof THREE.Mesh) {
enemyHitBoxes.push(child);
}
});
// 创建敌人血条UI
const hpBar = document.createElement('div');
hpBar.className = 'enemy-hp';
hpBar.id = `enemy-hp-${enemyId}`;
document.getElementById('ui-overlay').appendChild(hpBar);
}
}
// 设置事件监听
function setupEventListeners() {
const settingsButton = document.getElementById('settings-button');
const closeSettings = document.getElementById('close-settings');
const saveSettingsBtn = document.getElementById('save-settings');
const sensitivitySlider = document.getElementById('sensitivity-slider');
const speedSlider = document.getElementById('speed-slider');
const returnToDesktopBtn = document.getElementById('return-to-desktop');
const mapSelectButton = document.getElementById('map-select-button');
const closeMapSelect = document.getElementById('close-map-select');
const confirmMap = document.getElementById('confirm-map');
const mapOptions = document.querySelectorAll('.map-option');
const modeButtons = document.querySelectorAll('.mode-btn');
// 设置按钮
settingsButton.addEventListener('click', () => {
document.getElementById('settings-panel').style.display = 'block';
});
// 关闭设置
closeSettings.addEventListener('click', () => {
document.getElementById('settings-panel').style.display = 'none';
});
// 保存设置
saveSettingsBtn.addEventListener('click', () => {
gameSettings.sensitivity = sensitivitySlider.value / 1000;
gameSettings.moveSpeed = speedSlider.value / 100;
gameSettings.joystickMode = document.getElementById('joystick-mode').value;
gameSettings.collisionMode = document.getElementById('collision-mode').value;
// 更新玩家速度
if (player) {
player.speed = gameSettings.moveSpeed;
}
saveSettings();
document.getElementById('settings-panel').style.display = 'none';
showMessage("设置已保存", 1000);
});
// 返回桌面按钮
returnToDesktopBtn.addEventListener('click', function() {
// 隐藏设置面板
document.getElementById('settings-panel').style.display = 'none';
// 显示开始界面
document.getElementById('start-screen').style.display = 'flex';
// 重置游戏状态
gameActive = false;
showMessage("已返回主菜单", 1500);
});
// 灵敏度滑块
sensitivitySlider.addEventListener('input', () => {
document.getElementById('sensitivity-value').textContent = sensitivitySlider.value;
gameSettings.sensitivity = sensitivitySlider.value / 1000;
});
// 速度滑块
speedSlider.addEventListener('input', () => {
document.getElementById('speed-value').textContent = speedSlider.value;
gameSettings.moveSpeed = speedSlider.value / 100;
if (player) {
player.speed = gameSettings.moveSpeed;
}
});
// 地图选择按钮
mapSelectButton.addEventListener('click', () => {
document.getElementById('map-select-panel').style.display = 'block';
});
// 关闭地图选择
closeMapSelect.addEventListener('click', () => {
document.getElementById('map-select-panel').style.display = 'none';
});
// 地图选择
mapOptions.forEach(option => {
option.addEventListener('click', () => {
mapOptions.forEach(opt => opt.classList.remove('selected'));
option.classList.add('selected');
currentMap = option.dataset.map;
});
});
// 确认地图选择
confirmMap.addEventListener('click', () => {
document.getElementById('map-select-panel').style.display = 'none';
showMessage(`已选择: ${currentMap}地图`, 1500);
// 更新场景
if (scene) {
// 清除场景
while(scene.children.length > 0) {
scene.remove(scene.children[0]);
}
// 重新创建场景
createScene();
createGround();
addObstacles();
addVehicles();
// 重新添加玩家
scene.add(player.mesh);
player.mesh.add(camera);
// 重新添加敌人和队友
enemies.forEach(enemy => scene.add(enemy.mesh));
teammates.forEach(teammate => scene.add(teammate.mesh));
}
});
// 模式选择
modeButtons.forEach(button => {
button.addEventListener('click', () => {
modeButtons.forEach(btn => btn.classList.remove('active'));
button.classList.add('active');
gameMode = button.dataset.mode;
});
});
}
// 升级武器
function upgradeWeapon(weaponType) {
if (weaponUpgrades[weaponType].level < 5) {
weaponUpgrades[weaponType].level++;
weaponUpgrades[weaponType].damageMultiplier += 0.2;
showMessage(`${weapons[weaponType].name} 升级至 ${weaponUpgrades[weaponType].level}级!`, 2000);
updateWeaponInfo();
} else {
showMessage("武器已升至最高等级!", 1500);
}
}
// 使用手榴弹
function useGrenade() {
// 创建射线检测
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(new THREE.Vector2(), camera);
const intersects = raycaster.intersectObjects(objects);
if (intersects.length > 0) {
const impactPoint = intersects[0].point;
// 对范围内的敌人造成伤害
enemies.forEach(enemy => {
const distance = enemy.mesh.position.distanceTo(impactPoint);
if (distance < 15) {
// 根据距离减少伤害
const damage = Math.max(10, 50 - (distance * 2));
enemy.health -= damage;
updateEnemyHP(enemy);
if (enemy.health <= 0) {
removeEnemy(enemy);
}
}
});
// 创建爆炸效果
createExplosion(impactPoint);
showMessage("手榴弹爆炸!", 1000);
}
}
// 创建爆炸效果
function createExplosion(position) {
// 创建爆炸光源
const explosionLight = new THREE.PointLight(0xff6600, 10, 30);
explosionLight.position.copy(position);
scene.add(explosionLight);
// 添加爆炸粒子
for (let i = 0; i < 20; i++) {
const particle = {
position: position.clone(),
velocity: new THREE.Vector3(
(Math.random() - 0.5) * 3,
Math.random() * 2,
(Math.random() - 0.5) * 3
),
size: Math.random() * 0.5 + 0.2,
life: 1.0
};
particles.push(particle);
}
// 添加伤害指示器
const indicator = {
position: position.clone(),
size: 15,
life: 1.0
};
damageIndicators.push(indicator);
setTimeout(() => {
scene.remove(explosionLight);
}, 300);
}
// 键盘事件
function onKeyDown(e) {
if (!gameActive) return;
switch (e.key.toLowerCase()) {
case 'w': moveForward = true; break;
case 's': moveBackward = true; break;
case 'a': moveLeft = true; break;
case 'd': moveRight = true; break;
case 'r': reload(); break;
case ' ': shoot(); break;
case '1':
document.querySelector('.weapon-btn[data-weapon="pistol"]').click();
break;
case '2':
document.querySelector('.weapon-btn[data-weapon="shotgun"]').click();
break;
case '3':
document.querySelector('.weapon-btn[data-weapon="rifle"]').click();
break;
case '4':
document.querySelector('.weapon-btn[data-weapon="rocket"]').click();
break;
case 'g':
document.getElementById('grenade-btn').click();
break;
case 'h':
document.getElementById('heal-btn').click();
break;
case 'u':
document.querySelector('.upgrade-btn').click();
break;
case 'p':
document.getElementById('settings-button').click();
break;
}
}
function onKeyUp(e) {
if (!gameActive) return;
switch (e.key.toLowerCase()) {
case 'w': moveForward = false; break;
case 's': moveBackward = false; break;
case 'a': moveLeft = false; break;
case 'd': moveRight = false; break;
}
}
// 移除敌人
function removeEnemy(enemy) {
// 从场景中移除
scene.remove(enemy.mesh);
// 从敌人数组中移除
enemies = enemies.filter(e => e !== enemy);
// 从射线检测数组中移除
enemy.mesh.traverse(child => {
if (child instanceof THREE.Mesh) {
const index = enemyHitBoxes.indexOf(child);
if (index !== -1) enemyHitBoxes.splice(index, 1);
}
});
// 移除血条
const hpBar = document.getElementById(`enemy-hp-${enemy.id}`);
if (hpBar) hpBar.remove();
// 更新任务计数
enemiesKilled++;
updateMissionInfo();
// 根据难度调整分数
const points = difficulty === 'hard' ? 150 : difficulty === 'normal' ? 100 : 80;
playerScore += points;
totalKills++;
updateScore();
// 检查关卡完成
if (enemiesKilled >= missionEnemies) {
completeLevel();
}
}
// 射击函数 - 添加墙壁碰撞检测
function shoot() {
if (!gameActive) return;
if (isReloading || ammoCount < weapons[currentWeapon].ammoCost) {
showMessage(ammoCount < weapons[currentWeapon].ammoCost ? "需要装弹!" : "正在装弹...", 1000);
return;
}
ammoCount -= weapons[currentWeapon].ammoCost;
updateAmmoDisplay();
// 创建射线检测
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(new THREE.Vector2(), camera);
// 设置射程
raycaster.far = weapons[currentWeapon].range;
// 检测墙壁和敌人
const wallIntersects = raycaster.intersectObjects(walls);
const enemyIntersects = raycaster.intersectObjects(enemyHitBoxes);
// 找到最近的墙壁和敌人
const closestWall = wallIntersects[0];
const closestEnemy = enemyIntersects[0];
// 如果最近的墙壁比敌人近,则子弹被墙壁阻挡
if (closestWall && (!closestEnemy || closestWall.distance < closestEnemy.distance)) {
// 子弹被墙壁阻挡
if (currentWeapon === 'rocket') {
createExplosion(closestWall.point);
}
return;
}
// 击中敌人
if (closestEnemy) {
// 获取被击中的敌人ID
const enemyId = closestEnemy.object.userData.enemyId;
const enemy = enemies.find(e => e.id === enemyId);
if (enemy) {
// 应用武器升级的伤害加成
const damage = weapons[currentWeapon].damage * weaponUpgrades[currentWeapon].damageMultiplier;
enemy.health -= damage;
updateEnemyHP(enemy);
if (enemy.health <= 0) {
removeEn enemy(enemy);
}
}
}
// 添加枪口闪光效果
createMuzzleFlash();
// 如果是火箭筒,添加爆炸效果
if (currentWeapon === 'rocket') {
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(new THREE.Vector2(), camera);
raycaster.far = weapons['rocket'].range;
const intersects = raycaster.intersectObjects(objects);
if (intersects.length > 0) {
createExplosion(intersects[0].point);
}
}
}
// 完成关卡
function completeLevel() {
showMessage(`第 ${currentLevel} 关完成!`, 3000);
// 奖励
playerScore += currentLevel * 500;
updateScore();
// 准备下一关
setTimeout(() => {
currentLevel++;
resetGameState();
setupLevel();
}, 4000);
}
// 创建枪口火焰
function createMuzzleFlash() {
if (!player.weaponMesh) return;
const flashLight = new THREE.PointLight(0xffff66, 5, 15);
player.weaponMesh.add(flashLight);
// 添加枪口粒子
for (let i = 0; i < 5; i++) {
const particle = {
position: new THREE.Vector3(0, 0, 0),
velocity: new THREE.Vector3(
(Math.random() - 0.5) * 0.5,
(Math.random() - 0.5) * 0.5,
-1 - Math.random() * 0.5
),
size: Math.random() * 0.2 + 0.1,
life: 1.0
};
particles.push(particle);
}
setTimeout(() => {
if (flashLight.parent) {
flashLight.parent.remove(flashLight);
}
}, 50);
}
// 装弹函数
function reload() {
if (isReloading || ammoCount === 30 || totalAmmo === 0) return;
isReloading = true;
showMessage("装弹中...", 2000);
setTimeout(() => {
const neededAmmo = 30 - ammoCount;
const ammoToAdd = Math.min(neededAmmo, totalAmmo);
ammoCount += ammoToAdd;
totalAmmo -= ammoToAdd;
updateAmmoDisplay();
isReloading = false;
}, 2000);
}
// 更新任务信息
function updateMissionInfo() {
if (gameMode === 'campaign') {
document.getElementById('mission-info').textContent =
`任务: 消灭所有敌人 (${enemiesKilled}/${missionEnemies})`;
} else {
document.getElementById('mission-info').textContent =
`团队竞技: 消灭敌方 ${enemiesKilled}/${missionEnemies}`;
}
}
// 更新敌人血条
function updateEnemyHP(enemy) {
const hpBar = document.getElementById(`enemy-hp-${enemy.id}`);
if (hpBar) {
hpBar.style.width = `${50 * (enemy.health / (enemyTypes[enemy.type].health * difficultyFactors[difficulty].health))}px`;
}
}
// 更新弹药显示
function updateAmmoDisplay() {
document.getElementById('ammo-display').textContent = `弹药: ${ammoCount}/${totalAmmo}`;
}
// 更新分数显示
function updateScore() {
document.getElementById('score-display').textContent = `分数: ${playerScore}`;
}
// 更新血条
function updateHealthBar() {
const healthPercent = Math.max(0, playerHealth);
document.getElementById('health-fill').style.width = `${healthPercent}%`;
document.getElementById('health-text').textContent = `生命值: ${Math.round(healthPercent)}%`;
}
// 更新武器信息
function updateWeaponInfo() {
document.querySelector('.weapon-info').textContent =
`当前武器: ${weapons[currentWeapon].name} (${weaponUpgrades[currentWeapon].level}级)`;
}
// 显示消息
function showMessage(message, duration) {
const messageElement = document.getElementById('game-message');
messageElement.textContent = message;
messageElement.style.display = 'block';
setTimeout(() => {
messageElement.style.display = 'none';
}, duration);
}
// 更新小地图
function updateMiniMap() {
const mapContent = document.getElementById('map-content');
mapContent.innerHTML = '';
// 添加玩家位置
const playerPos = document.createElement('div');
playerPos.className = 'map-player';
playerPos.style.left = '50%';
playerPos.style.top = '50%';
mapContent.appendChild(playerPos);
// 添加敌人位置
enemies.forEach(enemy => {
// 计算相对位置
const relX = (enemy.mesh.position.x - player.mesh.position.x) / 10;
const relZ = (enemy.mesh.position.z - player.mesh.position.z) / 10;
// 确保在范围内
if (Math.abs(relX) < 75 && Math.abs(relZ) < 75) {
const enemyPos = document.createElement('div');
enemyPos.className = 'map-enemy';
enemyPos.style.left = `${50 + relX}%`;
enemyPos.style.top = `${50 + relZ}%`;
mapContent.appendChild(enemyPos);
}
});
// 添加队友位置
teammates.forEach(teammate => {
// 计算相对位置
const relX = (teammate.mesh.position.x - player.mesh.position.x) / 10;
const relZ = (teammate.mesh.position.z - player.mesh.position.z) / 10;
// 确保在范围内
if (Math.abs(relX) < 75 && Math.abs(relZ) < 75) {
const teammatePos = document.createElement('div');
teammatePos.className = 'map-player';
teammatePos.style.background = '#2ecc71';
teammatePos.style.left = `${50 + relX}%`;
teammatePos.style.top = `${50 + relZ}%`;
mapContent.appendChild(teammatePos);
}
});
}
// 检测碰撞
function checkCollisions() {
// 更新玩家碰撞盒
player.collisionBox.setFromObject(player.mesh);
for (const object of objects) {
const objectBox = new THREE.Box3().setFromObject(object);
if (player.collisionBox.intersectsBox(objectBox)) {
// 简单碰撞响应 - 将玩家推回
const direction = new THREE.Vector3();
direction.subVectors(player.mesh.position, object.position).normalize();
player.mesh.position.add(direction.multiplyScalar(0.3));
}
}
}
// 更新敌人位置
function updateEnemies(delta) {
if (!gameActive) return;
enemies.forEach(enemy => {
const distanceToPlayer = player.mesh.position.distanceTo(enemy.mesh.position);
// 敌人AI状态机
if (distanceToPlayer < 30) {
enemy.state = 'chase';
} else {
enemy.state = 'patrol';
}
if (distanceToPlayer < 15) {
enemy.state = 'attack';
}
// 根据状态执行行为
switch (enemy.state) {
case 'patrol':
// 巡逻行为
const directionToTarget = new THREE.Vector3();
directionToTarget.subVectors(enemy.patrolTarget, enemy.mesh.position).normalize();
enemy.mesh.rotation.y = Math.atan2(directionToTarget.x, directionToTarget.z);
enemy.mesh.position.x += directionToTarget.x * enemy.speed * delta * 60;
enemy.mesh.position.z += directionToTarget.z * enemy.speed * delta * 60;
// 如果到达目标点,选择新目标
if (enemy.mesh.position.distanceTo(enemy.patrolTarget) < 5) {
enemy.patrolTarget.set(
Math.random() * 200 - 100,
1.5,
Math.random() * 200 - 100
);
}
break;
case 'chase':
// 追逐玩家
const directionToPlayer = new THREE.Vector3();
directionToPlayer.subVectors(player.mesh.position, enemy.mesh.position).normalize();
enemy.mesh.rotation.y = Math.atan2(directionToPlayer.x, directionToPlayer.z);
enemy.mesh.position.x += directionToPlayer.x * enemy.speed * 1.5 * delta * 60;
enemy.mesh.position.z += directionToPlayer.z * enemy.speed * 1.5 * delta * 60;
break;
case 'attack':
// 攻击玩家
const attackDirection = new THREE.Vector3();
attackDirection.subVectors(player.mesh.position, enemy.mesh.position).normalize();
enemy.mesh.rotation.y = Math.atan2(attackDirection.x, attackDirection.z);
if (enemy.attackCooldown <= 0) {
playerHealth -= enemy.damage;
updateHealthBar();
enemy.attackCooldown = 60;
// 添加伤害指示器
const indicator = {
position: new THREE.Vector3(
(Math.random() - 0.5) * 100,
(Math.random() - 0.5) * 50,
0
),
size: 30,
life: 1.0
};
damageIndicators.push(indicator);
if (playerHealth <= 0) {
playerHealth = 0;
gameOver();
}
}
break;
}
if (enemy.attackCooldown > 0) {
enemy.attackCooldown -= delta * 60;
}
// 更新血条位置
const hpBar = document.getElementById(`enemy-hp-${enemy.id}`);
if (hpBar) {
const screenPosition = new THREE.Vector3();
screenPosition.copy(enemy.mesh.position);
screenPosition.y += 5;
screenPosition.project(camera);
const x = (screenPosition.x * 0.5 + 0.5) * window.innerWidth;
const y = (-screenPosition.y * 0.5 + 0.5) * window.innerHeight;
if (screenPosition.z < 1) {
hpBar.style.display = 'block';
hpBar.style.left = `${x - 25}px`;
hpBar.style.top = `${y - 30}px`;
} else {
hpBar.style.display = 'none';
}
}
});
}
// 更新队友行为
function updateTeammates(delta) {
if (!gameActive) return;
teammates.forEach(teammate => {
const distanceToPlayer = player.mesh.position.distanceTo(teammate.mesh.position);
// 队友AI状态机
if (distanceToPlayer > teammate.followDistance + 5) {
teammate.state = 'follow';
} else if (enemies.length > 0) {
teammate.state = 'attack';
} else {
teammate.state = 'idle';
}
// 根据状态执行行为
switch (teammate.state) {
case 'follow':
// 跟随玩家
const directionToPlayer = new THREE.Vector3();
directionToPlayer.subVectors(player.mesh.position, teammate.mesh.position).normalize();
teammate.mesh.rotation.y = Math.atan2(directionToPlayer.x, directionToPlayer.z);
teammate.mesh.position.x += directionToPlayer.x * teammate.speed * delta * 60;
teammate.mesh.position.z += directionToPlayer.z * teammate.speed * delta * 60;
break;
case 'attack':
// 寻找最近的敌人
let closestEnemy = null;
let minDistance = Infinity;
enemies.forEach(enemy => {
const distance = teammate.mesh.position.distanceTo(enemy.mesh.position);
if (distance < minDistance) {
minDistance = distance;
closestEnemy = enemy;
}
});
if (closestEnemy) {
// 攻击敌人
const directionToEnemy = new THREE.Vector3();
directionToEnemy.subVectors(closestEnemy.mesh.position, teammate.mesh.position).normalize();
teammate.mesh.rotation.y = Math.atan2(directionToEnemy.x, directionToEnemy.z);
if (teammate.attackCooldown <= 0 && minDistance < 30) {
closestEnemy.health -= teammate.damage;
updateEnemyHP(closestEnemy);
teammate.attackCooldown = 80;
if (closestEnemy.health <= 0) {
removeEnemy(closestEnemy);
}
}
}
break;
}
if (teammate.attackCooldown > 0) {
teammate.attackCooldown -= delta * 60;
}
// 更新血条位置
const hpBar = document.getElementById(`teammate-hp-${teammate.id}`);
if (hpBar) {
const screenPosition = new THREE.Vector3();
screenPosition.copy(teammate.mesh.position);
screenPosition.y += 5;
screenPosition.project(camera);
const x = (screenPosition.x * 0.5 + 0.5) * window.innerWidth;
const y = (-screenPosition.y * 0.5 + 0.5) * window.innerHeight;
if (screenPosition.z < 1) {
hpBar.style.display = 'block';
hpBar.style.left = `${x - 25}px`;
hpBar.style.top = `${y - 30}px`;
} else {
hpBar.style.display = 'none';
}
}
});
}
// 更新能力冷却
function updateAbilityCooldowns(delta) {
if (grenadeCooldown > 0) {
grenadeCooldown -= delta * 60;
const grenadeBtn = document.getElementById('grenade-btn');
if (grenadeCooldown > 0) {
if (!grenadeBtn.querySelector('.cooldown')) {
const cooldownEl = document.createElement('div');
cooldownEl.className = 'cooldown';
grenadeBtn.appendChild(cooldownEl);
}
grenadeBtn.querySelector('.cooldown').textContent = Math.ceil(grenadeCooldown/10);
} else {
const cooldownEl = grenadeBtn.querySelector('.cooldown');
if (cooldownEl) cooldownEl.remove();
}
}
if (healCooldown > 0) {
healCooldown -= delta * 60;
const healBtn = document.getElementById('heal-btn');
if (healCooldown > 0) {
if (!healBtn.querySelector('.cooldown')) {
const cooldownEl = document.createElement('div');
cooldownEl.className = 'cooldown';
healBtn.appendChild(cooldownEl);
}
healBtn.querySelector('.cooldown').textContent = Math.ceil(healCooldown/10);
} else {
const cooldownEl = healBtn.querySelector('.cooldown');
if (cooldownEl) cooldownEl.remove();
}
}
}
// 更新粒子效果
function updateParticles(delta) {
// 更新粒子
for (let i = particles.length - 1; i >= 0; i--) {
particles[i].position.add(particles[i].velocity);
particles[i].life -= delta * 2;
if (particles[i].life <= 0) {
particles.splice(i, 1);
}
}
// 更新伤害指示器
for (let i = damageIndicators.length - 1; i >= 0; i--) {
damageIndicators[i].life -= delta * 3;
if (damageIndicators[i].life <= 0) {
damageIndicators.splice(i, 1);
}
}
// 限制粒子数量
if (particles.length > 50) {
particles.splice(0, particles.length - 50);
}
}
// 渲染粒子效果
function renderParticles() {
// 渲染粒子
particles.forEach(p => {
const screenPosition = new THREE.Vector3();
screenPosition.copy(p.position);
screenPosition.project(camera);
const x = (screenPosition.x * 0.5 + 0.5) * window.innerWidth;
const y = (-screenPosition.y * 0.5 + 0.5) * window.innerHeight;
if (screenPosition.z < 1) {
const particle = document.createElement('div');
particle.className = 'damage-indicator';
particle.style.left = `${x}px`;
particle.style.top = `${y}px`;
particle.style.width = `${p.size * p.life * 10}px`;
particle.style.height = `${p.size * p.life * 10}px`;
particle.style.opacity = p.life;
document.getElementById('ui-overlay').appendChild(particle);
setTimeout(() => {
if (particle.parentNode) {
particle.parentNode.removeChild(particle);
}
}, 50);
}
});
// 渲染伤害指示器
damageIndicators.forEach(d => {
const indicator = document.createElement('div');
indicator.className = 'damage-indicator';
indicator.style.left = '50%';
indicator.style.top = '50%';
indicator.style.width = `${d.size * d.life}px`;
indicator.style.height = `${d.size * d.life}px`;
indicator.style.transform = `translate(-50%, -50%) translate(${d.position.x}px, ${d.position.y}px)`;
indicator.style.opacity = d.life;
document.getElementById('ui-overlay').appendChild(indicator);
setTimeout(() => {
if (indicator.parentNode) {
indicator.parentNode.removeChild(indicator);
}
}, 50);
});
}
// 计算FPS
function updateFPS() {
const now = performance.now();
const delta = now - lastFrameTime;
lastFrameTime = now;
fps = Math.round(1000 / delta);
document.getElementById('performance-stats').textContent = `FPS: ${fps}`;
}
// 窗口大小调整
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
// 游戏主循环
function animate() {
requestAnimationFrame(animate);
const delta = Math.min(clock.getDelta(), 0.1);
if (gameActive) {
// 玩家移动
velocity.x -= velocity.x * 0.1 * delta * 60;
velocity.z -= velocity.z * 0.1 * delta * 60;
direction.z = Number(moveForward) - Number(moveBackward);
direction.x = Number(moveRight) - Number(moveLeft);
direction.normalize();
if (moveForward || moveBackward) velocity.z -= direction.z * player.speed * delta * 60;
if (moveLeft || moveRight) velocity.x -= direction.x * player.speed * delta * 60;
player.mesh.translateX(velocity.x);
player.mesh.translateZ(velocity.z);
// 边界检查
player.mesh.position.x = Math.max(-100, Math.min(100, player.mesh.position.x));
player.mesh.position.z = Math.max(-100, Math.min(100, player.mesh.position.z));
// 检测碰撞
checkCollisions();
// 更新敌人
updateEnemies(delta);
// 更新队友
updateTeammates(delta);
// 更新小地图
updateMiniMap();
// 更新能力冷却
updateAbilityCooldowns(delta);
// 更新粒子
updateParticles(delta);
}
// 渲染场景
renderer.render(scene, camera);
// 渲染粒子效果
renderParticles();
// 更新FPS
updateFPS();
}
// 设置触摸控制
function setupTouchControls() {
const joystick = document.getElementById('touch-joystick');
const joystickKnob = document.getElementById('joystick-knob');
const shootButton = document.getElementById('shoot-button');
const reloadButton = document.getElementById('reload-button');
const startButton = document.getElementById('start-button');
const restartButton = document.getElementById('restart-button');
const grenadeBtn = document.getElementById('grenade-btn');
const healBtn = document.getElementById('heal-btn');
const weaponButtons = document.querySelectorAll('.weapon-btn');
const upgradeBtn = document.querySelector('.upgrade-btn');
// 开始按钮
startButton.addEventListener('click', startGame);
// 重新开始按钮
restartButton.addEventListener('click', restartGame);
// 武器切换
weaponButtons.forEach(btn => {
btn.addEventListener('click', function() {
weaponButtons.forEach(b => b.classList.remove('active'));
this.classList.add('active');
currentWeapon = this.dataset.weapon;
updateWeaponInfo();
// 更新武器模型
createWeaponModel(currentWeapon);
showMessage(`已切换武器: ${this.textContent.trim()}`, 1000);
// 触觉反馈
if (navigator.vibrate) {
navigator.vibrate(30);
}
});
});
// 升级武器按钮
upgradeBtn.addEventListener('click', function() {
if (playerScore >= 1000) {
upgradeWeapon(currentWeapon);
playerScore -= 1000;
updateScore();
// 触觉反馈
if (navigator.vibrate) {
navigator.vibrate([30, 20, 30]);
}
} else {
showMessage("积分不足!需要1000分升级武器", 1500);
}
});
// 手榴弹按钮
grenadeBtn.addEventListener('click', function() {
if (grenadeCooldown <= 0 && gameActive) {
useGrenade();
grenadeCooldown = 300; // 30秒冷却
// 触觉反馈
if (navigator.vibrate) {
navigator.vibrate(100);
}
}
});
// 治疗按钮
healBtn.addEventListener('click', function() {
if (healCooldown <= 0 && gameActive && playerHealth < 100) {
playerHealth = Math.min(100, playerHealth + 30);
updateHealthBar();
showMessage("生命值已恢复!", 1000);
healCooldown = 200; // 20秒冷却
// 触觉反馈
if (navigator.vibrate) {
navigator.vibrate([30, 50, 30]);
}
}
});
// 摇杆触摸事件 - 修复方向问题
joystick.addEventListener('touchstart', (e) => {
if (!gameActive) return;
e.preventDefault();
joystickActive = true;
touchStartPosition.set(e.touches[0].clientX, e.touches[0].clientY);
// 浮动摇杆模式
if (gameSettings.joystickMode === 'floating') {
joystick.style.left = `${e.touches[0].clientX - 70}px`;
joystick.style.bottom = `${window.innerHeight - e.touches[0].clientY - 70}px`;
}
});
document.addEventListener('touchmove', (e) => {
if (!joystickActive || !gameActive) return;
e.preventDefault();
const touch = e.touches[0];
const deltaX = touch.clientX - touchStartPosition.x;
const deltaY = touch.clientY - touchStartPosition.y;
// 限制摇杆移动范围(增大范围)
const distance = Math.min(Math.sqrt(deltaX * deltaX + deltaY * deltaY), 50);
const angle = Math.atan2(deltaY, deltaX);
joystickPosition.x = Math.cos(angle) * distance;
joystickPosition.y = Math.sin(angle) * distance;
// 更新摇杆位置
joystickKnob.style.transform = `translate(${joystickPosition.x}px, ${joystickPosition.y}px)`;
// 设置移动方向 - 增加死区
moveForward = joystickPosition.y < -15;
moveBackward = joystickPosition.y > 15;
moveLeft = joystickPosition.x < -15;
moveRight = joystickPosition.x > 15;
});
document.addEventListener('touchend', (e) => {
if (!joystickActive) return;
joystickActive = false;
joystickKnob.style.transform = 'translate(0, 0)';
moveForward = moveBackward = moveLeft = moveRight = false;
// 浮动摇杆模式重置位置
if (gameSettings.joystickMode === 'floating') {
joystick.style.left = '30px';
joystick.style.bottom = '30px';
}
});
// 右半屏视角控制 - 优化灵敏度
document.addEventListener('touchstart', (e) => {
if (!gameActive) return;
const touch = e.touches[0];
// 检查是否在右半屏
if (touch.clientX > window.innerWidth / 2) {
touchStartPosition.set(touch.clientX, touch.clientY);
}
});
document.addEventListener('touchmove', (e) => {
if (!gameActive || joystickActive) return;
e.preventDefault();
if (e.touches.length === 1) {
const touch = e.touches[0];
// 检查是否在右半屏
if (touch.clientX > window.innerWidth / 2) {
const movementX = touch.clientX - touchStartPosition.x;
const movementY = touch.clientY - touchStartPosition.y;
// 调整灵敏度 - 使用设置中的灵敏度值
player.mesh.rotation.y -= movementX * gameSettings.sensitivity;
camera.rotation.x -= movementY * gameSettings.sensitivity;
// 限制相机俯仰角度
camera.rotation.x = Math.max(-Math.PI/3, Math.min(Math.PI/3, camera.rotation.x));
touchStartPosition.set(touch.clientX, touch.clientY);
}
}
});
// 射击按钮 - 支持按住连续射击
let isShooting = false;
let shootInterval;
shootButton.addEventListener('touchstart', (e) => {
e.preventDefault();
if (gameActive) {
isShooting = true;
shoot();
// 根据武器射速设置连续射击
const weapon = weapons[currentWeapon];
shootInterval = setInterval(() => {
if (isShooting && gameActive) {
shoot();
}
}, weapon.fireRate * 1000);
// 触觉反馈
if (navigator.vibrate) {
navigator.vibrate(30);
}
}
});
shootButton.addEventListener('touchend', (e) => {
e.preventDefault();
isShooting = false;
clearInterval(shootInterval);
});
// 装弹按钮
reloadButton.addEventListener('touchstart', (e) => {
e.preventDefault();
if (gameActive) {
reload();
// 触觉反馈
if (navigator.vibrate) {
navigator.vibrate([30, 20, 30]);
}
}
});
}
// 启动游戏
window.onload = init;
</script>
</body>
</html>
index.html
style.css
index.js
index.html