游戏
This commit is contained in:
1 parent
da12f288d2
commit
c83bc17cfc
12 files changed
+7109
No files matched your search
@@ -0,0 +1,479 @@
|
||||
<!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, viewport-fit=cover, shrink-to-fit=yes">
|
||||
<title>深渊矿工 · 收集宝石游戏</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
user-select: none;
|
||||
}
|
||||
body {
|
||||
background: linear-gradient(180deg, #1a0e08 0%, #3b2a1a 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-family: 'Segoe UI', system-ui, sans-serif;
|
||||
padding: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
.game-container {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
background: radial-gradient(circle at 50% 40%, #5e3e1a, #2d1c0b);
|
||||
border-radius: 48px;
|
||||
box-shadow: 0 25px 45px rgba(0,0,0,0.7), inset 0 0 20px rgba(255,215,0,0.2);
|
||||
padding: 16px 12px 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
border: 1px solid #7a5c3a;
|
||||
}
|
||||
canvas {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border-radius: 32px;
|
||||
background: radial-gradient(ellipse at 50% 10%, #4a3620 0%, #1e1308 85%);
|
||||
box-shadow: inset 0 -12px 25px rgba(0,0,0,0.8), 0 10px 20px black;
|
||||
touch-action: none;
|
||||
}
|
||||
.info-panel {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 14px;
|
||||
padding: 0 10px;
|
||||
color: #ffd78b;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 2px 6px black;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
.score-board {
|
||||
background: rgba(0,0,0,0.6);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 50px;
|
||||
padding: 8px 22px;
|
||||
letter-spacing: 1px;
|
||||
box-shadow: 0 6px 14px rgba(0,0,0,0.7);
|
||||
border: 1px solid #c9a23b;
|
||||
color: #ffd966;
|
||||
}
|
||||
.restart-btn {
|
||||
background: #e6b422;
|
||||
border: none;
|
||||
color: #2b1a04;
|
||||
font-weight: bold;
|
||||
font-size: 1.2rem;
|
||||
padding: 10px 24px;
|
||||
border-radius: 40px;
|
||||
background: linear-gradient(180deg, #f9e076 0%, #ca9620 100%);
|
||||
box-shadow: 0 7px 0 #7a5200, 0 10px 20px black;
|
||||
transition: all 0.08s ease;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
cursor: pointer;
|
||||
border: 1px solid #ffe28c;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
.restart-btn:active {
|
||||
transform: translateY(4px);
|
||||
box-shadow: 0 3px 0 #7a5200, 0 7px 12px black;
|
||||
background: linear-gradient(180deg, #ebc85c 0%, #ad7d18 100%);
|
||||
}
|
||||
.hint {
|
||||
color: #dbbc6c;
|
||||
font-size: 0.8rem;
|
||||
margin-top: 6px;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="game-container">
|
||||
<canvas id="gameCanvas" width="360" height="600" aria-label="深渊矿工游戏画面"></canvas>
|
||||
<div class="info-panel">
|
||||
<div class="score-board" id="scoreDisplay">💎 0</div>
|
||||
<button class="restart-btn" id="restartButton">⛏️ 新矿洞</button>
|
||||
</div>
|
||||
<div class="hint">👆 左右滑动矿车 | 接宝石 ✦ 躲岩石</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const canvas = document.getElementById('gameCanvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const scoreSpan = document.getElementById('scoreDisplay');
|
||||
|
||||
const CW = 360;
|
||||
const CH = 600;
|
||||
canvas.width = CW;
|
||||
canvas.height = CH;
|
||||
|
||||
// ----- 游戏状态 -----
|
||||
let gameActive = true;
|
||||
let score = 0;
|
||||
|
||||
// 矿车属性
|
||||
const CART_WIDTH = 56;
|
||||
const CART_HEIGHT = 40;
|
||||
let cartX = CW / 2 - CART_WIDTH/2;
|
||||
const cartY = CH - 85; // 固定垂直位置
|
||||
|
||||
// 下落物品数组
|
||||
let items = [];
|
||||
|
||||
// 物品类型
|
||||
const TYPE_GEM = 'gem'; // 宝石 (+1分)
|
||||
const TYPE_ROCK = 'rock'; // 岩石 (碰撞结束)
|
||||
|
||||
const ITEM_SIZE = 34; // 统一尺寸
|
||||
const FALL_SPEED = 3.8;
|
||||
const SPAWN_RATE = 32; // 帧间隔
|
||||
let frameCount = 0;
|
||||
|
||||
// 触摸控制
|
||||
let targetCartX = cartX;
|
||||
let activeTouchId = null;
|
||||
|
||||
// ----- 辅助函数 -----
|
||||
function clampCart(value) {
|
||||
return Math.max(4, Math.min(CW - CART_WIDTH - 4, value));
|
||||
}
|
||||
|
||||
// 生成物品 (宝石或岩石)
|
||||
function spawnItem() {
|
||||
const maxX = CW - ITEM_SIZE;
|
||||
const x = Math.random() * maxX;
|
||||
// 70%概率宝石,30%岩石
|
||||
const type = Math.random() < 0.7 ? TYPE_GEM : TYPE_ROCK;
|
||||
items.push({
|
||||
x: x,
|
||||
y: -ITEM_SIZE - Math.random() * 15,
|
||||
size: ITEM_SIZE,
|
||||
type: type,
|
||||
});
|
||||
}
|
||||
|
||||
// 重置游戏
|
||||
function restartGame() {
|
||||
gameActive = true;
|
||||
score = 0;
|
||||
items = [];
|
||||
frameCount = 0;
|
||||
cartX = CW / 2 - CART_WIDTH/2;
|
||||
targetCartX = cartX;
|
||||
activeTouchId = null;
|
||||
updateScoreDisplay();
|
||||
}
|
||||
|
||||
function endGame() {
|
||||
if (!gameActive) return;
|
||||
gameActive = false;
|
||||
if (window.navigator && window.navigator.vibrate) {
|
||||
window.navigator.vibrate(40);
|
||||
}
|
||||
}
|
||||
|
||||
function updateScoreDisplay() {
|
||||
scoreSpan.textContent = `💎 ${score}`;
|
||||
}
|
||||
|
||||
// 碰撞检测
|
||||
function rectCollide(r1, r2) {
|
||||
return !(r2.x > r1.x + r1.w || r2.x + r2.w < r1.x ||
|
||||
r2.y > r1.y + r1.h || r2.y + r2.h < r1.y);
|
||||
}
|
||||
|
||||
// ----- 更新逻辑 -----
|
||||
function update() {
|
||||
if (!gameActive) return;
|
||||
|
||||
// 矿车平滑移动
|
||||
const diff = targetCartX - cartX;
|
||||
if (Math.abs(diff) > 0.3) {
|
||||
cartX += diff * 0.3;
|
||||
if (Math.abs(diff) < 0.6) cartX = targetCartX;
|
||||
} else {
|
||||
cartX = targetCartX;
|
||||
}
|
||||
cartX = clampCart(cartX);
|
||||
targetCartX = clampCart(targetCartX);
|
||||
|
||||
// 矿车碰撞矩形
|
||||
const cartRect = {
|
||||
x: cartX,
|
||||
y: cartY,
|
||||
w: CART_WIDTH,
|
||||
h: CART_HEIGHT,
|
||||
};
|
||||
|
||||
// 处理物品下落
|
||||
for (let i = items.length - 1; i >= 0; i--) {
|
||||
const item = items[i];
|
||||
item.y += FALL_SPEED;
|
||||
|
||||
const itemRect = {
|
||||
x: item.x,
|
||||
y: item.y,
|
||||
w: item.size,
|
||||
h: item.size,
|
||||
};
|
||||
|
||||
// 碰撞检测
|
||||
if (gameActive && rectCollide(cartRect, itemRect)) {
|
||||
if (item.type === TYPE_GEM) {
|
||||
// 收集宝石
|
||||
score++;
|
||||
updateScoreDisplay();
|
||||
items.splice(i, 1);
|
||||
continue;
|
||||
} else if (item.type === TYPE_ROCK) {
|
||||
// 撞到岩石,游戏结束
|
||||
endGame();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 移除超出屏幕的物品
|
||||
if (item.y > CH + 50) {
|
||||
items.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 生成新物品
|
||||
if (gameActive) {
|
||||
frameCount++;
|
||||
if (frameCount >= SPAWN_RATE) {
|
||||
spawnItem();
|
||||
frameCount = 0;
|
||||
// 动态微调
|
||||
const variation = Math.floor(Math.random() * 10) - 3;
|
||||
frameCount = -variation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----- 绘制图形 -----
|
||||
function draw() {
|
||||
ctx.clearRect(0, 0, CW, CH);
|
||||
|
||||
// 绘制岩壁纹理
|
||||
ctx.fillStyle = '#2f2416';
|
||||
for (let i = 0; i < 8; i++) {
|
||||
ctx.fillRect(0, i * 85 + (Date.now()*0.01 % 85), 8, 25);
|
||||
ctx.fillRect(CW-8, i * 85 + 30 + (Date.now()*0.01 % 70), 8, 25);
|
||||
}
|
||||
|
||||
// 绘制物品
|
||||
for (let item of items) {
|
||||
const { x, y, size, type } = item;
|
||||
ctx.save();
|
||||
ctx.shadowColor = '#00000080';
|
||||
ctx.shadowBlur = 8;
|
||||
|
||||
if (type === TYPE_GEM) {
|
||||
// 绘制宝石 (多面体)
|
||||
const grad = ctx.createLinearGradient(x, y, x+size, y+size);
|
||||
grad.addColorStop(0, '#b5f2ff');
|
||||
grad.addColorStop(0.4, '#3cc7e0');
|
||||
grad.addColorStop(1, '#0f5b6e');
|
||||
ctx.fillStyle = grad;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x + size/2, y);
|
||||
ctx.lineTo(x + size, y + size*0.6);
|
||||
ctx.lineTo(x + size*0.7, y + size);
|
||||
ctx.lineTo(x + size*0.3, y + size);
|
||||
ctx.lineTo(x, y + size*0.6);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
ctx.fillStyle = '#ffffffcc';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x + size/2, y+2);
|
||||
ctx.lineTo(x + size*0.8, y + size*0.55);
|
||||
ctx.lineTo(x + size/2, y + size*0.7);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
} else {
|
||||
// 绘制岩石
|
||||
const grad = ctx.createLinearGradient(x, y, x+6, y+size);
|
||||
grad.addColorStop(0, '#7d6b5a');
|
||||
grad.addColorStop(0.7, '#3e3325');
|
||||
ctx.fillStyle = grad;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x+5, y);
|
||||
ctx.lineTo(x+size-6, y+3);
|
||||
ctx.lineTo(x+size, y+size/2);
|
||||
ctx.lineTo(x+size-4, y+size-5);
|
||||
ctx.lineTo(x+size/2, y+size);
|
||||
ctx.lineTo(x+3, y+size-6);
|
||||
ctx.lineTo(x, y+size/3);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
ctx.fillStyle = '#5c5040';
|
||||
ctx.beginPath();
|
||||
ctx.arc(x+size/2-2, y+size/2-2, 4, 0, Math.PI*2);
|
||||
ctx.fill();
|
||||
}
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
// 绘制矿车
|
||||
ctx.save();
|
||||
ctx.shadowColor = '#000000';
|
||||
ctx.shadowBlur = 14;
|
||||
const cx = cartX;
|
||||
const cy = cartY;
|
||||
|
||||
// 矿车车身
|
||||
const cartGrad = ctx.createLinearGradient(cx, cy, cx, cy+CART_HEIGHT);
|
||||
cartGrad.addColorStop(0, '#b3823e');
|
||||
cartGrad.addColorStop(0.7, '#613d1c');
|
||||
ctx.fillStyle = cartGrad;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(cx+8, cy);
|
||||
ctx.lineTo(cx+CART_WIDTH-8, cy);
|
||||
ctx.lineTo(cx+CART_WIDTH-2, cy+CART_HEIGHT-8);
|
||||
ctx.lineTo(cx+CART_WIDTH-2, cy+CART_HEIGHT-2);
|
||||
ctx.lineTo(cx+2, cy+CART_HEIGHT-2);
|
||||
ctx.lineTo(cx+2, cy+CART_HEIGHT-8);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
||||
// 车轮
|
||||
ctx.fillStyle = '#2b1a0a';
|
||||
ctx.shadowBlur = 8;
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx+10, cy+CART_HEIGHT-6, 8, 0, Math.PI*2);
|
||||
ctx.fill();
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx+CART_WIDTH-10, cy+CART_HEIGHT-6, 8, 0, Math.PI*2);
|
||||
ctx.fill();
|
||||
ctx.fillStyle = '#5a5a5a';
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx+10, cy+CART_HEIGHT-6, 4, 0, Math.PI*2);
|
||||
ctx.fill();
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx+CART_WIDTH-10, cy+CART_HEIGHT-6, 4, 0, Math.PI*2);
|
||||
ctx.fill();
|
||||
|
||||
// 矿石点缀
|
||||
ctx.fillStyle = '#f9e076';
|
||||
ctx.shadowBlur = 6;
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx+15, cy+12, 5, 0, Math.PI*2);
|
||||
ctx.fill();
|
||||
ctx.fillStyle = '#ffb703';
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx+35, cy+18, 5, 0, Math.PI*2);
|
||||
ctx.fill();
|
||||
ctx.restore();
|
||||
|
||||
// 游戏结束画面
|
||||
if (!gameActive) {
|
||||
ctx.globalAlpha = 0.8;
|
||||
ctx.fillStyle = '#0f0803';
|
||||
ctx.fillRect(0, 0, CW, CH);
|
||||
ctx.globalAlpha = 1;
|
||||
ctx.fillStyle = '#f5d78c';
|
||||
ctx.font = 'bold 28px "Segoe UI", system-ui';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.shadowColor = 'black';
|
||||
ctx.shadowBlur = 18;
|
||||
ctx.fillText('⛔ 矿洞塌方', CW/2, CH/2-20);
|
||||
ctx.font = '18px system-ui';
|
||||
ctx.fillText('点击"新矿洞"继续', CW/2, CH/2+45);
|
||||
ctx.shadowBlur = 0;
|
||||
ctx.shadowColor = 'transparent';
|
||||
}
|
||||
}
|
||||
|
||||
// ----- 游戏循环 -----
|
||||
function gameLoop() {
|
||||
update();
|
||||
draw();
|
||||
requestAnimationFrame(gameLoop);
|
||||
}
|
||||
|
||||
// ----- 事件处理 -----
|
||||
function handleStart(e) {
|
||||
e.preventDefault();
|
||||
if (!gameActive) return;
|
||||
const clientX = e.touches ? e.touches[0].clientX : e.clientX;
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const scaleX = CW / rect.width;
|
||||
const relX = (clientX - rect.left) * scaleX;
|
||||
targetCartX = clampCart(relX - CART_WIDTH/2);
|
||||
cartX = targetCartX;
|
||||
if (e.touches) activeTouchId = e.touches[0].identifier;
|
||||
}
|
||||
|
||||
function handleMove(e) {
|
||||
e.preventDefault();
|
||||
if (!gameActive) return;
|
||||
if (e.touches) {
|
||||
if (activeTouchId === null) return;
|
||||
let touch = null;
|
||||
for (let i=0; i<e.touches.length; i++) {
|
||||
if (e.touches[i].identifier === activeTouchId) { touch = e.touches[i]; break; }
|
||||
}
|
||||
if (!touch) return;
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const scaleX = CW / rect.width;
|
||||
const relX = (touch.clientX - rect.left) * scaleX;
|
||||
targetCartX = clampCart(relX - CART_WIDTH/2);
|
||||
} else {
|
||||
if (e.buttons === 1) {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const scaleX = CW / rect.width;
|
||||
const relX = (e.clientX - rect.left) * scaleX;
|
||||
targetCartX = clampCart(relX - CART_WIDTH/2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleEnd(e) {
|
||||
e.preventDefault();
|
||||
if (e.touches && e.touches.length === 0) {
|
||||
activeTouchId = null;
|
||||
} else if (e.touches) {
|
||||
activeTouchId = e.touches[0].identifier;
|
||||
}
|
||||
}
|
||||
|
||||
function bindEvents() {
|
||||
canvas.addEventListener('touchstart', handleStart, { passive: false });
|
||||
canvas.addEventListener('touchmove', handleMove, { passive: false });
|
||||
canvas.addEventListener('touchend', handleEnd);
|
||||
canvas.addEventListener('touchcancel', handleEnd);
|
||||
canvas.addEventListener('mousedown', handleStart);
|
||||
window.addEventListener('mousemove', (e) => {
|
||||
if (e.buttons === 1 && gameActive) {
|
||||
e.preventDefault();
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const scaleX = CW / rect.width;
|
||||
const relX = (e.clientX - rect.left) * scaleX;
|
||||
targetCartX = clampCart(relX - CART_WIDTH/2);
|
||||
}
|
||||
});
|
||||
document.getElementById('restartButton').addEventListener('click', restartGame);
|
||||
document.getElementById('restartButton').addEventListener('touchstart', (e) => {
|
||||
e.preventDefault();
|
||||
restartGame();
|
||||
});
|
||||
}
|
||||
|
||||
bindEvents();
|
||||
restartGame();
|
||||
gameLoop();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user