登山赛车|无限地形 永不消失 完美版
by AtomicTurtle21332 lines12.1 KB
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登山赛车|无限地形 永不消失 完美版</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
background: linear-gradient(#72b9e8,#b8e9b8);
font-family: Arial,sans-serif;
overflow: hidden;
}
#gameContainer {
position: relative;
width: 100%;
max-width: 1200px;
height: 600px;
border:4px solid #222;
border-radius:8px;
overflow:hidden;
background: linear-gradient(#84c9f5,#bce8bc);
}
#scoreBoard {
position:absolute;top:15px;left:15px;
font-size:24px;font-weight:bold;
z-index:100;color:#111;
text-shadow: 1px 1px 3px #fff;
}
#carName{
position:absolute;top:50px;left:15px;font-size:20px;color:#222;font-weight:bold;
}
#gameOver {
position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
font-size:46px;color:#e63939;font-weight:bold;
text-shadow:2px 2px 3px #000;display:none;z-index:200;text-align:center;
background: rgba(0,0,0,0.5);
padding:30px;
border-radius:12px;
}
#gameOver span {display:block;font-size:22px;margin-top:8px;color:#fff;}
#car {
position:absolute;z-index:50;
border-radius:9px;
overflow: hidden;
}
.wheel {
position:absolute;width:21px;height:21px;background:#222;
border-radius:50%;border:3px solid #f1f1f1;
box-shadow:inset 0 0 4px #000;
}
#wheel1 {bottom:-10px;left:10px;}
#wheel2 {bottom:-10px;right:10px;}
#terrainCanvas {
position:absolute;bottom:0;left:0;width:100%;height:100%;
pointer-events:none;z-index:10;
}
.tips {margin-top:12px;font-size:16px;color:#222;text-align:center;}
.coin{
position:absolute;width:24px;height:24px;border-radius:50%;
background:radial-gradient(circle at 30% 30%,#fff9b0,#ffd700,#b8860b,#805000);
box-shadow:0 0 7px #ffcc22,inset -3px -3px 6px rgba(80,40,0,0.55);
animation:coinShine 1.2s infinite alternate ease-in-out;
z-index:35;
}
@keyframes coinShine{
from{box-shadow:0 0 5px #ffdd33,inset -3px -3px 6px rgba(80,40,0,0.55);}
to{box-shadow:0 0 12px #ffef55,inset -1px -1px 3px rgba(255,240,180,0.7);}
}
</style>
</head>
<body>
<div id="gameContainer">
<div id="scoreBoard">距离:0m|金币:0枚</div>
<div id="carName">当前车辆:家用轿车【按1~7切换车型】</div>
<div id="gameOver">车辆翻车!游戏结束!<span>按 R 重新开始</span></div>
<div id="car">
<div class="wheel" id="wheel1"></div>
<div class="wheel2" id="wheel2"></div>
</div>
<canvas id="terrainCanvas"></canvas>
</div>
<div class="tips">↑加速 ↓减速 R重开|1轿车 2吉普 3摩托 4重卡 5蹦蹦 6跑车 7桑塔纳</div>
<script>
const car = document.getElementById('car');
const scoreBoard = document.getElementById('scoreBoard');
const carNameDom = document.getElementById('carName');
const gameOver = document.getElementById('gameOver');
const canvas = document.getElementById('terrainCanvas');
const ctx = canvas.getContext('2d');
const gameBox = document.getElementById('gameContainer');
const w1 = document.getElementById('wheel1');
const w2 = document.getElementById('wheel2');
const carList = [
{name:"家用轿车",w:82,h:42,color:"#dd3838",acc:0.32,maxSp:7.2,roll:78,airRot:0.12},
{name:"越野吉普",w:88,h:48,color:"#2d882d",acc:0.38,maxSp:5.8,roll:92,airRot:0.07},
{name:"越野摩托",w:52,h:32,color:"#222",acc:0.48,maxSp:8.5,roll:52,airRot:0.19},
{name:"重型卡车",w:120,h:55,color:"#445577",acc:0.22,maxSp:4.2,roll:85,airRot:0.04},
{name:"沙滩蹦蹦",w:70,h:38,color:"#ff9922",acc:0.35,maxSp:6.3,roll:82,airRot:0.09},
{name:"竞速跑车",w:90,h:36,color:"#9922aa",acc:0.42,maxSp:10.2,roll:62,airRot:0.15},
{name:"大众桑塔纳",w:100,h:45,color:"#c1272d",acc:0.35,maxSp:6.8,roll:88,airRot:0.08}
];
let curCarIdx = 0;
let carCfg = carList[curCarIdx];
function setCarStyle(){
carCfg = carList[curCarIdx];
carNameDom.innerText = "当前车辆:"+carCfg.name+"【按1~7切换车型】";
car.style.width = carCfg.w+"px";
car.style.height = carCfg.h+"px";
car.style.background = `linear-gradient(${carCfg.color}, #881111)`;
if(curCarIdx === 6) {
car.style.boxShadow = `inset 0 -${carCfg.h*0.25}px 0 #222, inset 0 -4px 6px rgba(0,0,0,0.35)`;
} else {
car.style.boxShadow = `inset 0 -4px 6px rgba(0,0,0,0.35)`;
}
w1.style.left = (carCfg.w*0.12)+"px";
w2.style.right = (carCfg.w*0.12)+"px";
}
setCarStyle();
function resizeCanvas(){
canvas.width = gameBox.offsetWidth;
canvas.height = gameBox.offsetHeight;
}
resizeCanvas();
window.addEventListener('resize',resizeCanvas);
let gameRunning = true;
let distanceScore = 0;
let coinTotal = 0;
let carX = 150,carY = 200;
let carSpeedX = 0,carSpeedY = 0;
let carRotation = 0;
const gravity = 0.6;
const friction = 0.95;
let terrainPoints = [];
let coinList = [];
const keys = {};
function generateTerrain(){
terrainPoints = [];
coinList = [];
document.querySelectorAll('.coin').forEach(el=>el.remove());
let x=0;
let baseY = canvas.height - 100;
terrainPoints.push({x,y:baseY});
while(x < canvas.width * 10){
let rand = Math.random();
let type;
if(rand < 0.20) type = 0;
else if(rand < 0.75) type = 1;
else type = 2;
let segLength = 60 + Math.random()*80;
x += segLength;
switch(type){
case 0: baseY += (Math.random()-0.5)*15; break;
case 1: baseY += (Math.random()-0.5)*80; break;
case 2: baseY += (Math.random()-0.5)*110; break;
}
baseY = Math.max(canvas.height-240, Math.min(canvas.height-60, baseY));
terrainPoints.push({x,y:baseY});
if(Math.random()<0.4){
const coinY = baseY - 30 - Math.random()*70;
coinList.push({baseX:x, posX:x, posY:coinY, vy:0, collected:false, dom:null});
}
}
createCoinDom();
}
function createCoinDom(){
coinList.forEach(item=>{
if(item.collected) return;
const div = document.createElement('div');
div.className = 'coin';
gameBox.appendChild(div);
item.dom = div;
})
}
function getTerrainHeight(xPos){
if (xPos < 0) xPos = 0;
let last = terrainPoints[terrainPoints.length-1];
if (xPos > last.x) return last.y;
for(let i=0;i<terrainPoints.length-1;i++){
const p1=terrainPoints[i],p2=terrainPoints[i+1];
if(xPos>=p1.x && xPos<=p2.x){
let r = (xPos-p1.x)/(p2.x-p1.x);
return p1.y+(p2.y-p1.y)*r;
}
}
return canvas.height-50;
}
function drawTerrain(){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.fillStyle='#4c3627';
ctx.beginPath();
ctx.moveTo(0,canvas.height);
for(let p of terrainPoints){
ctx.lineTo(p.x-carX+150,p.y);
}
ctx.lineTo(canvas.width,canvas.height);
ctx.closePath();
ctx.fill();
ctx.fillStyle='#70523c';
ctx.beginPath();
ctx.moveTo(0,canvas.height);
for(let p of terrainPoints){
ctx.lineTo(p.x-carX+150,p.y-3);
}
ctx.lineTo(canvas.width,canvas.height);
ctx.closePath();
ctx.fill();
}
document.addEventListener('keydown',e=>{
keys[e.key]=true;
let num = parseInt(e.key);
if(num>=1&&num<=7){
curCarIdx = num-1;
setCarStyle();
}
});
document.addEventListener('keyup',e=>keys[e.key]=false);
function updateCoins(){
const carCenterX = carX+carCfg.w/2;
const carCenterY = carY+carCfg.h/2;
coinList.forEach(coin=>{
if(coin.collected || !coin.dom) return;
coin.vy += 0.35;
coin.posY += coin.vy;
const groundH = getTerrainHeight(coin.baseX);
if(coin.posY >= groundH-12){
coin.posY = groundH-12;
coin.vy = -coin.vy*0.32;
if(Math.abs(coin.vy)<0.4) coin.vy=0;
}
let screenX = coin.posX - carX + 150;
coin.dom.style.left = screenX+'px';
coin.dom.style.bottom = canvas.height - coin.posY +'px';
const dist = Math.hypot(carCenterX-coin.posX,carCenterY-coin.posY);
if(dist<32){
coin.collected=true;
coinTotal++;
coin.dom.style.display='none';
}
})
}
function updateGame(){
if(!gameRunning) return;
if(keys['ArrowUp']) carSpeedX+=carCfg.acc;
if(keys['ArrowDown']) carSpeedX-=carCfg.acc;
carSpeedX = Math.max(-carCfg.maxSp/2,Math.min(carCfg.maxSp,carSpeedX));
carSpeedX *= friction;
carSpeedY += gravity;
carX += carSpeedX;
carY += carSpeedY;
const w1H = getTerrainHeight(carX+carCfg.w*0.15);
const w2H = getTerrainHeight(carX+carCfg.w*0.85);
const groundY = Math.min(w1H,w2H);
if(carY+carCfg.h>=groundY){
carY = groundY-carCfg.h;
carSpeedY=0;
let ang = Math.atan2(w2H-w1H,carCfg.w*0.7);
carRotation = ang*180/Math.PI;
}else{
carRotation += carSpeedX * carCfg.airRot;
}
distanceScore = Math.floor(carX/10);
scoreBoard.textContent = `距离:${distanceScore}m|金币:${coinTotal}枚`;
if(Math.abs(carRotation) > carCfg.roll || carY > canvas.height + 500){
endGame();
}
car.style.left='150px';
car.style.bottom = canvas.height-carY+'px';
car.style.transform = `rotate(${carRotation}deg)`;
drawTerrain();
updateCoins();
requestAnimationFrame(updateGame);
}
function endGame(){
gameRunning=false;
gameOver.style.display='block';
}
function restart(){
gameRunning=true;
distanceScore=0;coinTotal=0;
carX=150;carY=200;carSpeedX=0;carSpeedY=0;carRotation=0;
gameOver.style.display='none';
generateTerrain();
drawTerrain();
}
document.addEventListener('keydown',e=>{
if(e.key.toLowerCase()==='r' && !gameRunning) restart();
})
generateTerrain();
updateGame();
</script>
</body>
</html>Game Source: 登山赛车|无限地形 永不消失 完美版
Creator: AtomicTurtle21
Libraries: none
Complexity: complex (332 lines, 12.1 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: game-atomicturtle21" to link back to the original. Then publish at arcadelab.ai/publish.