本教程将完整讲解如何开发一款和平精英风格的HTML射击游戏,涵盖核心设计理念、代码架构与关键实现细节。
核心设计架构
游戏机制系统
- 角色控制系统:通过键盘实现玩家移动
- 战斗系统:子弹发射与碰撞检测
- 道具系统:武器、弹药和医疗包收集
- 敌人系统:AI行为与伤害机制
- 界面系统:实时状态显示与游戏信息
技术选型
- HTML:构建游戏基础结构
- CSS:实现视觉效果与动画
- JavaScript:处理游戏逻辑与交互
代码实现详解
游戏HTML框架
<!DOCTYPE html>
<html>
<head><!-- 元数据与样式引入 -->
</head>
<body><div class="header">游戏信息显示</div><div class="game-container"><div class="game-area"><div class="player"></div><div class="enemy"></div><div class="item"></div><div class="bullet"></div></div><div class="stats-panel"><!-- 状态显示与操作控件 --></div></div>
</body>
</html>
样式设计要点
- 采用Flexbox/Grid现代化布局方案
- 运用CSS渐变与阴影效果
- 实现关键帧动画效果
- 响应式设计适配不同设备
JavaScript功能模块
- 游戏初始化模块
- 玩家控制模块
- 敌人生成与AI模块
- 道具系统模块
- 碰撞检测模块
- 界面更新模块
核心代码实现
游戏初始化
function initGame() {// 设置玩家初始位置playerX = gameArea.offsetWidth / 2;playerY = gameArea.offsetHeight / 2;resetGameElements();generateEnemies(5);generateItems(5);startMainLoop();initEventListeners();
}
敌人生成机制
function generateEnemies(count) {for (let i = 0; i < count; i++) {const safeDistance = 50;const x = safeDistance + Math.random() * (gameArea.offsetWidth - 2 * safeDistance);const y = safeDistance + Math.random() * (gameArea.offsetHeight - 2 * safeDistance);const enemy = document.createElement('div');enemy.className = 'enemy';enemy.style.left = `${x}px`;enemy.style.top = `${y}px`;gameArea.appendChild(enemy);enemies.push({element: enemy,position: {x, y},health: 30,speed: 1 + Math.random() * 2});}
}
碰撞检测系统
function detectCollisions() {// 玩家与敌人碰撞enemies.forEach(enemy => {const distance = calculateDistance(playerX, playerY, enemy.position.x, enemy.position.y);if (distance < 45) {health -= 2;updateHUD();if (health <= 0) endGame();}});// 子弹碰撞检测bullets.forEach((bullet, bIndex) => {enemies.forEach((enemy, eIndex) => {const distance = calculateDistance(bullet.x, bullet.y, enemy.position.x, enemy.position.y);if (distance < 25) {enemy.health -= 10;destroyBullet(bIndex);if (enemy.health <= 0) {removeEnemy(eIndex);score += 100;updateHUD();if (!enemies.length) nextLevel();}}});});
}
游戏主循环
function mainGameLoop() {updateEnemyPositions();detectCollisions();checkItemCollection();
}function updateBullets() {bullets.forEach((bullet, index) => {bullet.x += bullet.velocityX;bullet.y += bullet.velocityY;if (isOutsideBounds(bullet)) {removeBullet(index);return;}bullet.element.style.transform = `translate(${bullet.x}px, ${bullet.y}px)`;});
}
关键优化点
道具生成边界控制
// 确保道具在有效区域内生成
const margin = 40;
const x = margin + Math.random() * (gameArea.offsetWidth - 2 * margin);
const y = margin + Math.random() * (gameArea.offsetHeight - 2 * margin);
游戏元素清理
function resetGameElements() {[enemies, items, bullets].forEach(group => {group.forEach(item => {if (item.element?.parentNode) {gameArea.removeChild(item.element);}});group.length = 0;});
}
子弹系统优化
// 使用独立计时器管理子弹运动
bulletUpdateInterval = setInterval(updateBullets, 50);
完整代码
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>和平精英HTML</title><style>* {margin: 0;padding: 0;box-sizing: border-box;}body {font-family: 'Arial', sans-serif;background: linear-gradient(135deg, #0a2e36, #1a3e46);color: white;overflow: hidden;display: flex;flex-direction: column;height: 100vh;padding: 10px;}.header {text-align: center;padding: 10px;margin-bottom: 15px;background: rgba(0, 0, 0, 0.3);border-radius: 10px;box-shadow: 0 4px 15px rgba(0, 0, 0, 0.5);}.title {font-size: 28px;font-weight: bold;color: #ffcc00;text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7);margin-bottom: 5px;}.game-container {display: flex;flex: 1;gap: 15px;margin-bottom: 15px;}.game-area {flex: 1;background-color: #1d4e5c;border-radius: 10px;position: relative;overflow: hidden;box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);background-image: radial-gradient(circle at 25% 25%, #2a5c6c 0%, #1d4e5c 20%, #0f3b48 100%);}.player {width: 40px;height: 40px;background: linear-gradient(135deg, #ff9d00, #ff6600);border: 3px solid #ffcc00;border-radius: 50%;position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);z-index: 100;transition: transform 0.2s;box-shadow: 0 0 10px rgba(255, 157, 0, 0.7);}.player::after {content: '';position: absolute;width: 10px;height: 10px;background: #ff3300;border-radius: 50%;top: 50%;left: 70%;transform: translateY(-50%);}.weapon {width: 25px;height: 10px;background: linear-gradient(135deg, #555, #333);position: absolute;top: 50%;left: 70%;transform: translateY(-50%);border-radius: 3px;}.bullet {position: absolute;width: 8px;height: 8px;background: radial-gradient(circle, #ffcc00, #ff6600);border-radius: 50%;z-index: 90;box-shadow: 0 0 5px #ffcc00;}.enemy {position: absolute;width: 35px;height: 35px;background: radial-gradient(circle at 30% 30%, #ff3c3c, #cc0000);border: 2px solid #ff0000;border-radius: 50%;display: flex;align-items: center;justify-content: center;color: white;font-weight: bold;z-index: 80;box-shadow: 0 0 10px rgba(255, 0, 0, 0.5);}.item {position: absolute;width: 30px;height: 30px;border-radius: 5px;display: flex;align-items: center;justify-content: center;color: white;font-weight: bold;font-size: 18px;z-index: 70;animation: pulse 2s infinite;box-shadow: 0 0 10px rgba(255, 255, 255, 0.5);border: 1px solid rgba(255, 255, 255, 0.3);}@keyframes pulse {0% { transform: scale(1) rotate(0deg); }50% { transform: scale(1.1) rotate(5deg); }100% { transform: scale(1) rotate(0deg); }}.weapon-item {background: linear-gradient(135deg, #7d5ba6, #5a3f80);}.health-item {background: linear-gradient(135deg, #29bf12, #1e8c0c);}.ammo-item {background: linear-gradient(135deg, #f2bb05, #d9a404);}.stats-panel {width: 250px;background: linear-gradient(135deg, #1d4e5c, #0f3b48);border-radius: 10px;padding: 15px;display: flex;flex-direction: column;gap: 15px;box-shadow: 0 0 15px rgba(0, 0, 0, 0.4);}.panel-title {font-size: 18px;color: #ffcc00;border-bottom: 2px solid #2a8c79;padding-bottom: 5px;margin-bottom: 10px;}.stat {display: flex;align-items: center;gap: 10px;}.stat-icon {width: 30px;height: 30px;border-radius: 5px;display: flex;align-items: center;justify-content: center;background: linear-gradient(135deg, #2a8c79, #1d6b5c);font-weight: bold;box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);}.stat-value {flex: 1;height: 20px;background-color: #0a2e36;border-radius: 5px;overflow: hidden;box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.5);}.stat-fill {height: 100%;border-radius: 5px;transition: width 0.3s;}.health-fill {background: linear-gradient(90deg, #29bf12, #8eff80);width: 100%;}.ammo-fill {background: linear-gradient(90deg, #f2bb05, #ffdd80);width: 100%;}.inventory {display: grid;grid-template-columns: repeat(2, 1fr);gap: 10px;margin-top: 10px;}.inventory-item {height: 60px;background: linear-gradient(135deg, #2a8c79, #1d6b5c);border-radius: 5px;display: flex;flex-direction: column;align-items: center;justify-content: center;gap: 5px;box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);border: 1px solid rgba(255, 255, 255, 0.1);}.inventory-icon {font-size: 20px;}.inventory-title {font-size: 12px;}.controls {display: flex;gap: 10px;margin-top: 15px;}.btn {flex: 1;padding: 12px;border: none;border-radius: 5px;background: linear-gradient(135deg, #2a8c79, #1d6b5c);color: white;font-weight: bold;cursor: pointer;transition: all 0.3s;box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);}.btn:hover {transform: translateY(-2px);box-shadow: 0 5px 10px rgba(0, 0, 0, 0.4);}.btn:active {transform: translateY(0);}.btn-fire {background: linear-gradient(135deg, #f21d44, #c50d2e);}.btn-fire:hover {background: linear-gradient(135deg, #ff3c60, #d91a3d);}.game-status {text-align: center;padding: 10px;background: linear-gradient(135deg, #1d4e5c, #0f3b48);border-radius: 10px;margin-bottom: 15px;box-shadow: 0 0 15px rgba(0, 0, 0, 0.4);}.score {font-size: 24px;font-weight: bold;color: #f2bb05;text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.5);}.message {position: absolute;top: 20px;left: 50%;transform: translateX(-50%);background-color: rgba(0, 0, 0, 0.7);padding: 10px 20px;border-radius: 5px;z-index: 200;opacity: 0;transition: opacity 0.5s;font-size: 14px;text-align: center;max-width: 80%;}.show {opacity: 1;}.instructions {background: rgba(0, 0, 0, 0.3);padding: 10px;border-radius: 5px;margin-top: 10px;font-size: 12px;}.key {display: inline-block;background: rgba(0, 0, 0, 0.5);padding: 2px 5px;border-radius: 3px;margin: 0 2px;border: 1px solid rgba(255, 255, 255, 0.2);}</style>
</head>
<body><div class="header"><div class="title">和平精英HTML版</div><div class="score">分数: <span id="score">0</span></div></div><div class="game-container"><div class="game-area" id="gameArea"><div class="player" id="player"><div class="weapon"></div></div><div class="message" id="message"></div><!-- 敌人和物品将由JavaScript动态生成 --></div><div class="stats-panel"><div class="panel-title">玩家状态</div><div class="stat"><div class="stat-icon">❤️</div><div class="stat-value"><div class="stat-fill health-fill" id="healthBar"></div></div></div><div class="stat"><div class="stat-icon">🔫</div><div class="stat-value"><div class="stat-fill ammo-fill" id="ammoBar"></div></div></div><div class="panel-title">背包</div><div class="inventory"><div class="inventory-item"><div class="inventory-icon">🔫</div><div class="inventory-title" id="weaponName">无</div></div><div class="inventory-item"><div class="inventory-icon">❤️</div><div class="inventory-title" id="healthCount">0</div></div><div class="inventory-item"><div class="inventory-icon">📦</div><div class="inventory-title" id="ammoCount">0</div></div><div class="inventory-item"><div class="inventory-icon">⭐</div><div class="inventory-title" id="level">1</div></div></div><div class="controls"><button class="btn" id="btnReload">装弹 (R)</button><button class="btn btn-fire" id="btnFire">开火 (F)</button></div><div class="instructions">移动: <span class="key">W</span> <span class="key">A</span> <span class="key">S</span> <span class="key">D</span>或方向键<br>开火: <span class="key">F</span> 或 点击按钮<br>装弹: <span class="key">R</span><br>使用医疗包: <span class="key">H</span></div></div></div><script>// 游戏变量const player = document.getElementById('player');const gameArea = document.getElementById('gameArea');const message = document.getElementById('message');const scoreElement = document.getElementById('score');const healthBar = document.getElementById('healthBar');const ammoBar = document.getElementById('ammoBar');const weaponName = document.getElementById('weaponName');const healthCount = document.getElementById('healthCount');const ammoCount = document.getElementById('ammoCount');const levelElement = document.getElementById('level');const btnReload = document.getElementById('btnReload');const btnFire = document.getElementById('btnFire');let playerX = 400;let playerY = 300;let score = 0;let health = 100;let ammo = 30;let maxAmmo = 30;let playerLevel = 1;let playerWeapon = '无';let healthKits = 0;let ammoPacks = 0;let enemies = [];let items = [];let bullets = [];let gameInterval;let bulletInterval;// 初始化游戏function initGame() {// 设置玩家初始位置playerX = gameArea.offsetWidth / 2;playerY = gameArea.offsetHeight / 2;player.style.left = `${playerX}px`;player.style.top = `${playerY}px`;// 清除所有现有的敌人和物品clearGameElements();// 生成敌人和物品generateEnemies(5);generateItems(5);// 更新UIupdateUI();// 开始游戏循环startGameLoop();// 添加键盘事件监听setupEventListeners();showMessage('游戏开始!收集装备,消灭敌人!');}// 清除游戏元素function clearGameElements() {enemies.forEach(enemy => {if (enemy.element && enemy.element.parentNode) {gameArea.removeChild(enemy.element);}});items.forEach(item => {if (item.element && item.element.parentNode) {gameArea.removeChild(item.element);}});bullets.forEach(bullet => {if (bullet.element && bullet.element.parentNode) {gameArea.removeChild(bullet.element);}});// 重置数组enemies = [];items = [];bullets = [];}// 设置事件监听器function setupEventListeners() {document.addEventListener('keydown', handleKeyPress);btnReload.addEventListener('click', reloadWeapon);btnFire.addEventListener('click', fireWeapon);}// 开始游戏循环function startGameLoop() {if (gameInterval) clearInterval(gameInterval);if (bulletInterval) clearInterval(bulletInterval);gameInterval = setInterval(gameLoop, 100);bulletInterval = setInterval(moveBullets, 50);}// 生成敌人function generateEnemies(count) {for (let i = 0; i < count; i++) {const enemy = document.createElement('div');enemy.className = 'enemy';// 确保敌人生成在游戏区域内const padding = 50;const x = padding + Math.random() * (gameArea.offsetWidth - 2 * padding);const y = padding + Math.random() * (gameArea.offsetHeight - 2 * padding);enemy.style.left = `${x}px`;enemy.style.top = `${y}px`;enemy.textContent = '☠️';gameArea.appendChild(enemy);enemies.push({element: enemy,x: x,y: y,health: 30,speed: 1 + Math.random() * 2});}}// 生成物品function generateItems(count) {const itemTypes = [{ class: 'weapon-item', symbol: '🔫', type: 'weapon' },{ class: 'health-item', symbol: '❤️', type: 'health' },{ class: 'ammo-item', symbol: '📦', type: 'ammo' }];for (let i = 0; i < count; i++) {const type = itemTypes[Math.floor(Math.random() * itemTypes.length)];const item = document.createElement('div');item.className = `item ${type.class}`;item.textContent = type.symbol;// 确保物品生成在游戏区域内const padding = 40;const x = padding + Math.random() * (gameArea.offsetWidth - 2 * padding);const y = padding + Math.random() * (gameArea.offsetHeight - 2 * padding);item.style.left = `${x}px`;item.style.top = `${y}px`;gameArea.appendChild(item);items.push({element: item,x: x,y: y,type: type.type});}}// 游戏主循环function gameLoop() {moveEnemies();checkCollisions();checkItemPickups();}// 移动敌人function moveEnemies() {enemies.forEach(enemy => {// 计算方向向量const dx = playerX - enemy.x;const dy = playerY - enemy.y;// 计算距离const distance = Math.sqrt(dx * dx + dy * dy);// 标准化并应用速度if (distance > 0) {enemy.x += (dx / distance) * enemy.speed;enemy.y += (dy / distance) * enemy.speed;}// 更新DOM位置enemy.element.style.left = `${enemy.x}px`;enemy.element.style.top = `${enemy.y}px`;});}// 移动子弹function moveBullets() {bullets.forEach((bullet, index) => {// 更新子弹位置bullet.x += bullet.speedX;bullet.y += bullet.speedY;// 检查子弹是否超出游戏区域if (bullet.x < 0 || bullet.x > gameArea.offsetWidth || bullet.y < 0 || bullet.y > gameArea.offsetHeight) {if (bullet.element.parentNode) {gameArea.removeChild(bullet.element);}bullets.splice(index, 1);return;}// 更新DOM位置bullet.element.style.left = `${bullet.x}px`;bullet.element.style.top = `${bullet.y}px`;});}// 检查碰撞function checkCollisions() {// 检查敌人与玩家的碰撞enemies.forEach((enemy) => {const dx = playerX - enemy.x;const dy = playerY - enemy.y;const distance = Math.sqrt(dx * dx + dy * dy);if (distance < 45) {// 玩家受到伤害health -= 2;updateUI();if (health <= 0) {gameOver();}}});// 检查子弹与敌人的碰撞bullets.forEach((bullet, bulletIndex) => {enemies.forEach((enemy, enemyIndex) => {const dx = bullet.x - enemy.x;const dy = bullet.y - enemy.y;const distance = Math.sqrt(dx * dx + dy * dy);if (distance < 25) {// 击中敌人enemy.health -= 10;// 移除子弹if (bullet.element.parentNode) {gameArea.removeChild(bullet.element);}bullets.splice(bulletIndex, 1);// 检查敌人是否被消灭if (enemy.health <= 0) {if (enemy.element.parentNode) {gameArea.removeChild(enemy.element);}enemies.splice(enemyIndex, 1);// 增加分数score += 100;updateUI();// 如果所有敌人都被消灭,生成新的敌人if (enemies.length === 0) {playerLevel++;generateEnemies(5 + playerLevel);generateItems(3);showMessage(`等级提升!当前等级: ${playerLevel}`);updateUI();}}}});});}// 检查物品拾取function checkItemPickups() {items.forEach((item, index) => {const dx = playerX - item.x;const dy = playerY - item.y;const distance = Math.sqrt(dx * dx + dy * dy);if (distance < 35) {// 拾取物品if (item.element.parentNode) {gameArea.removeChild(item.element);}items.splice(index, 1);// 根据物品类型应用效果switch (item.type) {case 'weapon':playerWeapon = 'M416';maxAmmo = 30;ammo = maxAmmo;showMessage('获得了 M416 步枪!');break;case 'health':healthKits += 1;showMessage('获得了医疗包!');break;case 'ammo':ammoPacks += 1;showMessage('获得了弹药包!');break;}updateUI();}});}// 发射武器function fireWeapon() {if (playerWeapon === '无') {showMessage('没有武器!');return;}if (ammo <= 0) {showMessage('需要装弹!');return;}// 创建子弹const bullet = document.createElement('div');bullet.className = 'bullet';// 设置子弹初始位置(玩家前方)const bulletSize = 8;const bulletX = playerX + 30;const bulletY = playerY;bullet.style.left = `${bulletX}px`;bullet.style.top = `${bulletY}px`;gameArea.appendChild(bullet);// 设置子弹方向(向右移动)const speed = 5;// 添加子弹到数组bullets.push({element: bullet,x: bulletX,y: bulletY,speedX: speed,speedY: 0});// 减少弹药ammo--;updateUI();}// 装弹function reloadWeapon() {if (playerWeapon === '无') {showMessage('没有武器!');return;}if (ammoPacks <= 0) {showMessage('没有弹药包!');return;}ammoPacks--;ammo = maxAmmo;showMessage('装弹完成!');updateUI();}// 处理按键事件function handleKeyPress(e) {const speed = 10;switch (e.key.toLowerCase()) {case 'arrowup':case 'w':playerY = Math.max(20, playerY - speed);break;case 'arrowdown':case 's':playerY = Math.min(gameArea.offsetHeight - 20, playerY + speed);break;case 'arrowleft':case 'a':playerX = Math.max(20, playerX - speed);break;case 'arrowright':case 'd':playerX = Math.min(gameArea.offsetWidth - 20, playerX + speed);break;case 'f':fireWeapon();break;case 'r':reloadWeapon();break;case 'h':useHealthKit();break;}// 更新玩家位置player.style.left = `${playerX}px`;player.style.top = `${playerY}px`;}// 使用医疗包function useHealthKit() {if (healthKits <= 0) {showMessage('没有医疗包!');return;}healthKits--;health = Math.min(100, health + 50);showMessage('使用了医疗包!');updateUI();}// 更新UIfunction updateUI() {scoreElement.textContent = score;healthBar.style.width = `${health}%`;ammoBar.style.width = `${(ammo / maxAmmo) * 100}%`;weaponName.textContent = playerWeapon;healthCount.textContent = healthKits;ammoCount.textContent = ammoPacks;levelElement.textContent = playerLevel;}// 显示消息function showMessage(text) {message.textContent = text;message.classList.add('show');setTimeout(() => {message.classList.remove('show');}, 2000);}// 游戏结束function gameOver() {clearInterval(gameInterval);clearInterval(bulletInterval);showMessage(`游戏结束!最终得分: ${score}`);document.removeEventListener('keydown', handleKeyPress);// 3秒后重新开始游戏setTimeout(initGame, 3000);}// 启动游戏window.onload = initGame;</script>
</body>
</html>