Enduro 克隆游戏 — 基于 HTML、CSS 与 JavaScript 的完整教程模板
·
Enduro 克隆游戏 — 基于 HTML、CSS 与 JavaScript 的完整教程模板
目录
项目概述
Enduro 克隆游戏 是对经典街机赛车 Enduro 的前端复刻。游戏以纯 HTML、CSS 与原生 JavaScript 实现,模拟车辆在无尽道路上的行驶、超车与躲避障碍的体验。该项目既可作为教学示例,也便于扩展加入更多功能。
主要特性
- 纯前端实现:仅使用 HTML、CSS 和 JavaScript,无需后端或数据库。
- 复古 UI:模仿经典 Enduro 外观,屏幕上实时显示分数和进度。
- 响应式布局:可适应不同屏幕尺寸。
- 核心游戏玩法:
- 玩家控制一辆赛车在无尽赛道上行驶。
- 使用键盘进行平滑的左右移动,并可控制速度(加速/减速)。
- 随着游戏进程,速度和难度不断增加。
- 模拟原版 Enduro 的昼夜过渡效果。
- 游戏机制:
- 超越一定数量赛车可晋级下一阶段。
- 碰撞系统:与障碍物(其他车辆)碰撞会导致速度减慢。
- 跟踪距离和时间。
- 交通密度逐渐增加,提升挑战性。
- 视觉和音频增强:
- 像素艺术风格图形,营造怀旧感。
- 模拟道路运动的动画背景。
- 流畅的汽车运动和碰撞动画。
- 引人入胜的引擎、碰撞和过渡音效(若有实现)。
- 教育用途:该项目是练习和巩固基本编程概念,提升 JavaScript 游戏开发技能的宝贵资源。
技术栈与需求
- 核心语言:JavaScript
- 前端技术:HTML (用于结构), CSS (用于样式)
- 编码工具:Notepad++ 或任何可以运行 HTML 文件的文本编辑器(如 Visual Studio Code, Sublime Text 等)。
- 运行环境:任何现代 Web 浏览器(如 Chrome, Firefox, Edge, Safari)。
- 数据库:无。
- 项目类型:Web 应用程序。
环境准备
本游戏作为纯前端 Web 应用,无需复杂的后端或数据库配置。
- 文本编辑器:请确保您已安装并熟悉使用一款文本编辑器,例如 Visual Studio Code、Sublime Text 或 Notepad++。
- 现代浏览器:准备一个您常用的现代浏览器,用于打开和运行游戏。
获取并运行项目
- 下载源代码:通常,项目源代码会以
.zip压缩包的形式提供。请从可靠来源(如 SourceCodester 网站)下载本游戏的源代码。 - 解压文件:将下载的
.zip文件解压到您选择的任意目录中。解压后,您会得到一个包含游戏所有文件和文件夹的项目目录。 - 运行游戏:
- 在解压后的项目文件夹中,找到主 HTML 文件(通常命名为
index.html)。 - 双击
index.html文件,它将自动在您的默认浏览器中打开,游戏即可启动。 - 或者,您也可以在浏览器中选择“文件” -> “打开文件”,然后导航到
index.html并打开。 - (可选)为了避免本地文件系统限制,您可以使用轻量级本地服务器(如 VS Code 的 Live Server 扩展或 Node.js 的
http-server)来运行项目。
- 在解压后的项目文件夹中,找到主 HTML 文件(通常命名为
项目结构示例
enduro-clone/
├─ index.html # 游戏主页面
├─ css/ # 存放 CSS 样式文件
│ └─ style.css # 游戏样式表
├─ js/ # 存放 JavaScript 文件
│ └─ game.js # 游戏核心逻辑
├─ assets/ # 存放游戏资源,如图片和音效(可选)
│ ├─ images/
│ │ ├─ car.png
│ │ └─ obstacle.png
│ └─ audio/
│ ├─ engine.mp3
│ └─ crash.mp3
└─ README.md # 项目说明文件 (当前正在阅读的)
示例代码片段
以下代码为可直接复制到项目中的基础实现示例。这些片段旨在展示核心结构,实际项目可能包含更多细节和优化。
index.html 示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Enduro 克隆游戏</title>
<link rel="stylesheet" href="css/style.css" />
</head>
<body>
<div id="game-root">
<header id="hud">
<div id="score">分数: <span id="score-value">0</span></div>
<div id="stage">关卡: <span id="stage-value">1</span></div>
</header>
<main id="game-area">
<!-- 游戏元素(由 JS 动态插入) -->
<div id="player" class="car"></div>
</main>
<div id="overlay" class="hidden">
<div id="game-over">
<h1>游戏结束</h1>
<p>最终得分:<span id="final-score">0</span></p>
<button id="btn-restart">重新开始</button>
</div>
</div>
</div>
<script src="js/game.js"></script>
</body>
</html>
style.css 示例
/* css/style.css */
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }
body {
background: #222; /* 主背景色 */
color: #fff;
font-family: "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden; /* 防止滚动条 */
}
#game-root {
width: 800px;
max-width: 95vw; /* 适应小屏幕 */
border: 2px solid #fff;
box-shadow: 0 0 15px rgba(0,0,0,0.5);
background-color: #333;
}
#hud {
display: flex;
justify-content: space-between;
padding: 10px;
font-size: 1.2em;
background-color: #444;
border-bottom: 1px solid #666;
}
#score, #stage {
padding: 5px 10px;
background-color: #555;
border-radius: 4px;
}
#game-area {
position: relative;
width: 100%; /* 继承父元素宽度 */
height: 600px; /* 游戏区域固定高度 */
background: linear-gradient(#444, #222); /* 简单道路背景 */
overflow: hidden; /* 隐藏超出边界的元素 */
}
/* 玩家车辆 */
.car {
position: absolute;
width: 50px; /* 车辆宽度 */
height: 90px; /* 车辆高度 */
background: red; /* 车辆颜色 */
bottom: 30px; /* 距离底部 */
left: calc(50% - 25px); /* 水平居中 */
border-radius: 6px;
transition: left 0.1s linear; /* 平滑左右移动 */
box-shadow: 0 0 10px rgba(255,255,255,0.3);
}
/* 障碍物(其他车辆) */
.obstacle {
position: absolute;
width: 48px;
height: 48px;
background: gray; /* 障碍物颜色 */
border-radius: 4px;
box-shadow: 0 0 8px rgba(0,0,0,0.5);
}
/* 覆盖层(游戏结束) */
#overlay {
position: absolute;
inset: 0; /* 覆盖整个游戏区域 */
display: flex;
justify-content: center;
align-items: center;
background: rgba(0,0,0,0.7); /* 半透明黑色背景 */
z-index: 100; /* 确保在最上层 */
}
#overlay.hidden { display: none; } /* 默认隐藏 */
#game-over {
background: #2a2a2a;
padding: 30px 40px;
border-radius: 10px;
text-align: center;
box-shadow: 0 0 20px rgba(0,255,255,0.5);
transform: scale(0.9);
animation: popIn 0.3s ease-out forwards;
}
@keyframes popIn {
from { transform: scale(0.7); opacity: 0; }
to { transform: scale(1); opacity: 1; }
}
#game-over h1 {
margin-bottom: 15px;
color: #ffcc00;
font-size: 2.5em;
text-shadow: 2px 2px 4px rgba(0,0,0,0.7);
}
#game-over p {
margin-bottom: 25px;
font-size: 1.5em;
}
#game-over #final-score {
font-weight: bold;
color: #00ff00;
}
#btn-restart {
padding: 12px 25px;
font-size: 1.2em;
cursor: pointer;
border: none;
border-radius: 5px;
background: #007bff;
color: #fff;
transition: background 0.3s ease, transform 0.1s ease;
box-shadow: 0 4px 6px rgba(0,0,0,0.3);
}
#btn-restart:hover {
background: #0056b3;
transform: translateY(-2px);
}
#btn-restart:active {
transform: translateY(0);
box-shadow: 0 2px 3px rgba(0,0,0,0.3);
}
game.js 示例
// js/game.js
document.addEventListener('DOMContentLoaded', () => {
// --- DOM 元素获取 ---
const player = document.getElementById('player');
const gameArea = document.getElementById('game-area');
const scoreValue = document.getElementById('score-value');
const stageValue = document.getElementById('stage-value'); // 关卡显示
const overlay = document.getElementById('overlay');
const finalScore = document.getElementById('final-score');
const restartBtn = document.getElementById('btn-restart');
// --- 游戏状态变量 ---
let score = 0;
let currentStage = 1;
let gameRunning = false; // 游戏是否正在进行
let animationFrameId; // 用于 requestAnimationFrame
let playerX = gameArea.clientWidth / 2 - player.clientWidth / 2; // 玩家X坐标
const playerSpeed = 8; // 玩家左右移动速度
let gameSpeed = 5; // 游戏整体下落速度 (像素/帧)
const obstacleGenerationInterval = 60; // 障碍物生成间隔帧数
let frameCount = 0; // 帧计数器
// --- 初始化游戏 ---
function initGame() {
// 重置状态
score = 0;
currentStage = 1;
gameSpeed = 5;
gameRunning = true;
scoreValue.textContent = score;
stageValue.textContent = currentStage;
overlay.classList.add('hidden'); // 隐藏游戏结束界面
// 重置玩家位置
playerX = gameArea.clientWidth / 2 - player.clientWidth / 2;
player.style.left = `${playerX}px`;
player.style.bottom = '30px'; // 确保在底部
// 清除所有现有障碍物
document.querySelectorAll('.obstacle').forEach(obs => obs.remove());
// 启动游戏循环
if (animationFrameId) cancelAnimationFrame(animationFrameId); // 取消之前的循环
gameLoop();
}
// --- 游戏主循环 ---
function gameLoop() {
if (!gameRunning) return;
frameCount++;
// 障碍物生成逻辑
if (frameCount % obstacleGenerationInterval === 0) {
createObstacle();
}
// 移动障碍物并进行碰撞检测
document.querySelectorAll('.obstacle').forEach(obstacle => {
let currentTop = parseFloat(obstacle.style.top);
obstacle.style.top = `${currentTop + gameSpeed}px`;
// 障碍物超出底部,移除并加分
if (currentTop > gameArea.clientHeight) {
obstacle.remove();
score++;
scoreValue.textContent = score;
// 简单关卡晋升逻辑 (例如每得20分升一级)
if (score % 20 === 0 && score !== 0) {
currentStage++;
stageValue.textContent = currentStage;
gameSpeed += 0.5; // 增加游戏速度,提升难度
}
} else {
// 碰撞检测
if (isColliding(player, obstacle)) {
gameOver();
}
}
});
animationFrameId = requestAnimationFrame(gameLoop);
}
// --- 创建障碍物 ---
function createObstacle() {
const obstacle = document.createElement('div');
obstacle.className = 'obstacle';
obstacle.style.top = '-50px'; // 从顶部之外开始生成
// 随机左右位置,确保不会超出边界
const randomX = Math.random() * (gameArea.clientWidth - 48); // 48为障碍物宽度
obstacle.style.left = `${randomX}px`;
gameArea.appendChild(obstacle);
}
// --- 碰撞检测函数 ---
function isColliding(element1, element2) {
const rect1 = element1.getBoundingClientRect();
const rect2 = element2.getBoundingClientRect();
// 检查是否有任何重叠
return !(
rect1.bottom < rect2.top ||
rect1.top > rect2.bottom ||
rect1.right < rect2.left ||
rect1.left > rect2.right
);
}
// --- 游戏结束逻辑 ---
function gameOver() {
gameRunning = false;
cancelAnimationFrame(animationFrameId); // 停止动画循环
finalScore.textContent = score;
overlay.classList.remove('hidden'); // 显示游戏结束界面
}
// --- 事件监听器 ---
document.addEventListener('keydown', (e) => {
if (!gameRunning) return; // 游戏未进行时,不响应按键
// 玩家左右移动
if (e.key === 'ArrowLeft') {
playerX -= playerSpeed;
} else if (e.key === 'ArrowRight') {
playerX += playerSpeed;
}
// 限制玩家在游戏区域内
playerX = Math.max(0, Math.min(playerX, gameArea.clientWidth - player.clientWidth));
player.style.left = `${playerX}px`;
});
restartBtn.addEventListener('click', initGame);
// --- 首次加载时启动游戏 ---
initGame();
});
操作与控制
- 向左移动:按下键盘上的
左箭头 (←)键。 - 向右移动:按下键盘上的
右箭头 (→)键。 - 加速(若实现):原始描述中提到“上箭头键加速”,但在提供的示例代码中未直接实现加速功能,而是通过得分增加
gameSpeed来提升难度。您可以在game.js中添加对ArrowUp键的监听来控制玩家车辆的垂直移动速度或游戏全局速度。 - 减速(若实现):原始描述中提到“下箭头键减速”,同样未在示例代码中直接实现。您可以在
game.js中添加对ArrowDown键的监听。 - 重新开始:游戏结束后,点击屏幕中央的“重新开始”按钮。
常见问题与调试提示
- 游戏无法运行:
- 文件路径:请确保
index.html中link和script标签的href或src属性指向的 CSS 和 JavaScript 文件路径正确(例如css/style.css,js/game.js)。 - 浏览器控制台:打开浏览器开发者工具(通常按
F12),切换到“控制台 (Console)”选项卡。如果代码有错误,这里会显示详细的错误信息,帮助您定位问题。
- 文件路径:请确保
- 图片或资源未加载:
- 如果您的项目使用了图片或音频文件,请检查其在 CSS 或 JavaScript 中引用的路径是否正确,并确认文件确实存在于相应位置。
- 本地服务器:直接从文件系统(
file:///协议)打开 HTML 文件时,浏览器可能会实施 CORS(跨域资源共享)安全策略,导致某些资源(尤其是从外部加载的)无法加载。强烈建议使用本地静态服务器来运行项目(如 VS Code 的 Live Server 扩展,或通过 Node.js 安装http-server:npm install -g http-server,然后在项目根目录运行http-server)。
- 移动卡顿或不流畅:
- 游戏循环:确保游戏的主循环使用
requestAnimationFrame而不是setInterval。requestAnimationFrame会与浏览器刷新率同步,提供更流畅的动画。 - DOM 操作:频繁的 DOM 操作会影响性能。尽量减少在游戏循环中直接操作 DOM 的次数。例如,可以通过改变元素的
transform属性来移动,而不是直接改变top或left。 - 性能分析:在浏览器开发者工具中,使用“性能 (Performance)”选项卡录制一段游戏运行过程,可以帮助您找出性能瓶颈。
- 游戏循环:确保游戏的主循环使用
扩展与改进建议
这个 Enduro 克隆项目是一个优秀的起点,您可以尝试以下方式来进一步探索和提升:
- 视觉增强:
- 滚动道路背景:实现背景图片或 CSS 渐变的平滑垂直滚动,模拟道路在移动。
- 白天/夜晚循环:更精细地控制背景颜色和亮度的变化,模拟时间流逝。
- 车辆贴图:替换单色车辆为真实的像素艺术贴图。
- 粒子效果:在车辆加速时添加尘土或尾气效果。
- 游戏性增强:
- 不同障碍物:引入不同大小、形状或行为模式的障碍物(例如,卡车、摩托车)。
- 道具系统:添加拾取道具(如加速、护盾、分数加成)的功能。
- 生命值/碰撞惩罚:除了减速,可以引入生命值系统,碰撞时扣除生命值,生命值为零时游戏结束。
- 更复杂的关卡设计:不仅仅是速度提升,还可以改变障碍物的生成模式或密度。
- 音效与背景音乐:为碰撞、得分、加速、游戏开始/结束等事件添加音效,并播放循环背景音乐。
- 用户体验:
- 开始菜单:添加一个更完善的开始界面,包含游戏说明、音量控制等。
- 高分榜:使用
localStorage在本地存储玩家的高分记录。 - 键盘配置:允许玩家自定义控制按键。
- 代码优化:
- 模块化:将游戏的不同部分(如游戏逻辑、渲染、输入处理)拆分为独立的函数或模块,提高可维护性。
- 面向对象:可以尝试使用面向对象编程的思想,为玩家、障碍物等创建类。
截图说明示例
| 描述 | 截图示例 |
|---|---|
| 游戏主界面:玩家在赛道起点准备开始。 | ![]() |
| 游戏进行中:玩家在躲避障碍物。 | ![]() |
| 游戏结束画面:显示最终得分与重玩选项。 | ![]() |
许可证与来源说明
- 源代码来源:本教程模板改编自 SourceCodester 网站上名为“使用 HTML、CSS 和 JavaScript 的 Enduro 克隆游戏”的项目。
- 教育用途:根据原始项目说明,此应用程序仅供教育用途。
- 许可证:请根据原始项目提供方的具体说明,遵守其源代码的使用和分发条款。
更多推荐






所有评论(0)