<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>3D 跑酷游戏</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
touch-action: manipulation;
}
body {
margin: 0;
padding: 0;
overflow: hidden;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
color: #333;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
height: 100vh;
width: 100vw;
}
#gameContainer {
position: relative;
width: 100vw;
height: 100vh;
}
#startScreen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #43cea2 0%, #185a9d 100%);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 30;
transition: opacity 0.5s ease;
}
#startScreen h1 {
color: white;
font-size: 3.5rem;
margin-bottom: 20px;
text-shadow: 0 2px 10px rgba(0,0,0,0.3);
text-align: center;
}
#startScreen p {
color: rgba(255, 255, 255, 0.9);
font-size: 1.2rem;
max-width: 80%;
text-align: center;
margin-bottom: 30px;
line-height: 1.6;
}
#startBtn {
background: #ff6b6b;
border: none;
color: white;
padding: 15px 40px;
font-size: 1.3rem;
border-radius: 50px;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
font-weight: bold;
letter-spacing: 1px;
}
#startBtn:hover {
background: #ff5252;
transform: translateY(-3px);
box-shadow: 0 6px 20px rgba(0,0,0,0.3);
}
#ui {
position: absolute;
top: 20px;
left: 20px;
z-index: 10;
background: rgba(255, 255, 255, 0.85);
padding: 15px 20px;
border-radius: 12px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
backdrop-filter: blur(10px);
}
#score {
font-size: 22px;
font-weight: bold;
color: #2c3e50;
margin-bottom: 8px;
}
#distance {
font-size: 18px;
color: #34495e;
}
#gameOver {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(255, 255, 255, 0.95);
padding: 30px;
border-radius: 15px;
text-align: center;
display: none;
z-index: 20;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
backdrop-filter: blur(10px);
width: 80%;
max-width: 400px;
}
#gameOver h2 {
color: #e74c3c;
font-size: 2.5rem;
margin-top: 0;
margin-bottom: 20px;
}
#finalStats {
font-size: 1.2rem;
margin: 20px 0;
color: #2c3e50;
}
#restartBtn {
background: #2ecc71;
border: none;
color: white;
padding: 12px 30px;
font-size: 1.1rem;
border-radius: 50px;
cursor: pointer;
transition: all 0.3s ease;
font-weight: bold;
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
}
#restartBtn:hover {
background: #27ae60;
transform: translateY(-2px);
box-shadow: 0 6px 15px rgba(0,0,0,0.15);
}
#instructions {
position: absolute;
bottom: 20px;
left: 20px;
background: rgba(255, 255, 255, 0.85);
padding: 15px;
border-radius: 12px;
font-size: 14px;
max-width: 300px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(10px);
z-index: 15;
}
#instructions h3 {
margin-top: 0;
color: #2c3e50;
margin-bottom: 10px;
}
.key {
display: inline-block;
background: #3498db;
color: white;
padding: 3px 8px;
border-radius: 4px;
margin: 0 3px;
font-weight: bold;
}
#mobileControls {
position: absolute;
bottom: 20px;
width: 100%;
display: none;
justify-content: space-around;
padding: 0 20px;
z-index: 15;
}
.mobile-btn {
width: 70px;
height: 70px;
background: rgba(255, 255, 255, 0.8);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
font-size: 24px;
font-weight: bold;
color: #3498db;
box-shadow: 0 4px 10px rgba(0,0,0,0.2);
user-select: none;
cursor: pointer;
backdrop-filter: blur(5px);
}
.mobile-btn:active {
background: rgba(236, 240, 241, 0.9);
transform: scale(0.95);
}
#jumpBtn {
background: rgba(231, 76, 60, 0.8);
color: white;
}
@media (max-width: 768px) {
#instructions {
display: none;
}
#mobileControls {
display: flex;
}
#startScreen h1 {
font-size: 2.5rem;
}
#startScreen p {
font-size: 1rem;
max-width: 90%;
}
}
</style>
</head>
<body>
<div id="gameContainer">
<div id="startScreen">
<h1>3D 跑酷游戏</h1>
<p>在城市中奔跑,收集金币,避开障碍物!挑战你的反应速度和技巧。</p>
<button id="startBtn">开始游戏</button>
</div>
<div id="ui">
<div id="score">得分: 0</div>
<div>距离: <span id="distance">0</span>m</div>
</div>
<div id="instructions">
<h3>操作说明</h3>
<p>使用 <span class="key">←</span> <span class="key">→</span> 键左右移动</p>
<p>按下 <span class="key">空格</span> 键跳跃</p>
<p>收集金币,避开障碍物!</p>
</div>
<div id="mobileControls">
<div class="mobile-btn" id="leftBtn">←</div>
<div class="mobile-btn" id="jumpBtn">↑</div>
<div class="mobile-btn" id="rightBtn">→</div>
</div>
<div id="gameOver">
<h2>游戏结束</h2>
<div id="finalStats">
<p>最终得分: <span id="finalScore">0</span></p>
<p>跑酷距离: <span id="finalDistance">0</span>m</p>
</div>
<button id="restartBtn">重新开始</button>
</div>
</div>
<!-- 引入 Three.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
// 游戏变量
let scene, camera, renderer;
let player, ground, obstacles = [], coins = [];
let score = 0;
let distance = 0;
let gameSpeed = 0.3;
let gameOver = false;
let gameStarted = false;
let playerLane = 0; // -1:左 0:中 1:右
let isJumping = false;
let jumpHeight = 0;
let jumpSpeed = 0.3;
let gravity = 0.02;
let isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
// 初始化游戏
function init() {
// 创建场景
scene = new THREE.Scene();
scene.background = new THREE.Color(0x87CEEB);
scene.fog = new THREE.Fog(0x87CEEB, 20, 100);
// 创建相机
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 5, 10);
camera.lookAt(0, 0, 0);
// 创建渲染器
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.getElementById('gameContainer').appendChild(renderer.domElement);
// 添加光源
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(10, 20, 15);
directionalLight.castShadow = true;
scene.add(directionalLight);
// 创建地面
const groundGeometry = new THREE.PlaneGeometry(30, 300);
const groundMaterial = new THREE.MeshStandardMaterial({
color: 0x2E8B57,
roughness: 0.8,
metalness: 0.2
});
ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.rotation.x = -Math.PI / 2;
ground.receiveShadow = true;
scene.add(ground);
// 添加地面纹理
const gridHelper = new THREE.GridHelper(30, 30, 0x000000, 0x000000);
gridHelper.position.y = 0.01;
scene.add(gridHelper);
// 创建玩家
const playerGeometry = new THREE.BoxGeometry(1, 1, 1);
const playerMaterial = new THREE.MeshStandardMaterial({
color: 0xFF4136,
roughness: 0.7,
metalness: 0.3
});
player = new THREE.Mesh(playerGeometry, playerMaterial);
player.position.set(0, 0.5, 0);
player.castShadow = true;
scene.add(player);
// 添加环境元素
createEnvironment();
// 事件监听
window.addEventListener('keydown', handleKeyDown);
window.addEventListener('resize', onWindowResize);
document.getElementById('startBtn').addEventListener('click', startGame);
document.getElementById('restartBtn').addEventListener('click', restartGame);
// 移动端控制
if (isMobile) {
document.getElementById('leftBtn').addEventListener('touchstart', () => movePlayer(-1));
document.getElementById('rightBtn').addEventListener('touchstart', () => movePlayer(1));
document.getElementById('jumpBtn').addEventListener('touchstart', jump);
}
// 开始游戏循环
animate();
}
// 开始游戏
function startGame() {
document.getElementById('startScreen').style.display = 'none';
gameStarted = true;
gameOver = false;
}
// 创建环境元素
function createEnvironment() {
// 添加背景建筑
for (let i = 0; i < 20; i++) {
const buildingHeight = Math.random() * 10 + 5;
const buildingGeometry = new THREE.BoxGeometry(
Math.random() * 3 + 2,
buildingHeight,
Math.random() * 3 + 2
);
const buildingMaterial = new THREE.MeshStandardMaterial({
color: new THREE.Color(Math.random() * 0x808080 + 0x404040),
roughness: 0.9
});
const building = new THREE.Mesh(buildingGeometry, buildingMaterial);
building.position.set(
(Math.random() - 0.5) * 40,
buildingHeight / 2,
-Math.random() * 200 - 20
);
building.castShadow = true;
scene.add(building);
}
}
// 处理键盘输入
function handleKeyDown(event) {
if (!gameStarted || gameOver) return;
switch(event.key) {
case 'ArrowLeft':
movePlayer(-1);
break;
case 'ArrowRight':
movePlayer(1);
break;
case ' ':
jump();
break;
}
}
// 移动玩家
function movePlayer(direction) {
if (!gameStarted || gameOver) return;
if (direction === -1 && playerLane > -1) {
playerLane--;
} else if (direction === 1 && playerLane < 1) {
playerLane++;
}
}
// 跳跃
function jump() {
if (!gameStarted || gameOver || isJumping) return;
isJumping = true;
jumpHeight = 0.5;
}
// 创建障碍物
function createObstacle() {
const lane = Math.floor(Math.random() * 3) - 1; // -1, 0, or 1
const obstacleGeometry = new THREE.CylinderGeometry(0.5, 0.5, 1, 16);
const obstacleMaterial = new THREE.MeshStandardMaterial({
color: 0x8B4513,
roughness: 0.8
});
const obstacle = new THREE.Mesh(obstacleGeometry, obstacleMaterial);
obstacle.position.set(lane * 4, 0.5, -30);
obstacle.castShadow = true;
scene.add(obstacle);
obstacles.push(obstacle);
}
// 创建金币
function createCoin() {
const lane = Math.floor(Math.random() * 3) - 1; // -1, 0, or 1
const coinGeometry = new THREE.CylinderGeometry(0.5, 0.5, 0.1, 16);
const coinMaterial = new THREE.MeshStandardMaterial({
color: 0xFFD700,
roughness: 0.3,
metalness: 0.8
});
const coin = new THREE.Mesh(coinGeometry, coinMaterial);
coin.position.set(lane * 4, 1.5, -30);
coin.rotation.x = Math.PI / 2;
coin.castShadow = true;
scene.add(coin);
coins.push(coin);
}
// 更新游戏状态
function update() {
if (!gameStarted || gameOver) return;
// 增加距离和速度
distance += gameSpeed;
document.getElementById('distance').textContent = Math.floor(distance);
// 增加游戏难度
if (distance % 100 === 0) {
gameSpeed += 0.01;
}
// 更新玩家位置
player.position.x = playerLane * 4;
// 处理跳跃
if (isJumping) {
player.position.y += jumpHeight;
jumpHeight -= gravity;
if (player.position.y <= 0.5) {
player.position.y = 0.5;
isJumping = false;
jumpHeight = 0;
}
}
// 移动障碍物和金币
for (let i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].position.z += gameSpeed;
// 移除超出视野的障碍物
if (obstacles[i].position.z > 10) {
scene.remove(obstacles[i]);
obstacles.splice(i, 1);
}
// 检查碰撞
else if (
Math.abs(obstacles[i].position.x - player.position.x) < 1.5 &&
Math.abs(obstacles[i].position.z - player.position.z) < 1.5 &&
player.position.y < 1.5
) {
endGame();
}
}
for (let i = coins.length - 1; i >= 0; i--) {
coins[i].position.z += gameSpeed;
coins[i].rotation.z += 0.1;
// 移除超出视野的金币
if (coins[i].position.z > 10) {
scene.remove(coins[i]);
coins.splice(i, 1);
}
// 检查收集
else if (
Math.abs(coins[i].position.x - player.position.x) < 1.5 &&
Math.abs(coins[i].position.z - player.position.z) < 1.5 &&
Math.abs(coins[i].position.y - player.position.y) < 1.5
) {
scene.remove(coins[i]);
coins.splice(i, 1);
score += 10;
document.getElementById('score').textContent = `得分: ${score}`;
}
}
// 随机生成障碍物和金币
if (Math.random() < 0.03) {
createObstacle();
}
if (Math.random() < 0.02) {
createCoin();
}
}
// 游戏结束
function endGame() {
gameOver = true;
document.getElementById('finalScore').textContent = score;
document.getElementById('finalDistance').textContent = Math.floor(distance);
document.getElementById('gameOver').style.display = 'block';
}
// 重新开始游戏
function restartGame() {
// 重置游戏状态
gameOver = false;
score = 0;
distance = 0;
gameSpeed = 0.3;
playerLane = 0;
isJumping = false;
player.position.set(0, 0.5, 0);
// 清除障碍物和金币
obstacles.forEach(obstacle => scene.remove(obstacle));
coins.forEach(coin => scene.remove(coin));
obstacles = [];
coins = [];
// 更新UI
document.getElementById('score').textContent = `得分: ${score}`;
document.getElementById('distance').textContent = '0';
document.getElementById('gameOver').style.display = 'none';
}
// 窗口大小调整
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
// 动画循环
function animate() {
requestAnimationFrame(animate);
update();
// 相机跟随玩家
camera.position.x = player.position.x;
camera.position.z = player.position.z + 10;
camera.lookAt(player.position.x, player.position.y, player.position.z - 5);
renderer.render(scene, camera);
}
// 启动游戏
init();
</script>
</body>
</html>
index.html
index.html