轴向漂移:移动端响应式物理迷宫游戏
来自 Wikiprompt,自由的提示词百科全书
轴向漂移:移动端响应式物理迷宫游戏 一个用于生成单文件HTML5迷宫游戏的全面提示,该游戏具有重力切换机制、移动倾斜控制和包豪斯视觉风格。
提示词内容收藏
🌐
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Axial Drift · Bauhaus maze</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #1a1a1a;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
font-family: 'Inter', 'Helvetica Neue', system-ui, sans-serif;
touch-action: none;
overflow: hidden;
}
.game-frame {
background: #f5f0e6;
padding: 12px;
border-radius: 24px;
box-shadow: 0 20px 40px rgba(0,0,0,0.6);
}
canvas {
display: block;
width: min(92vw, 92vh);
height: min(92vw, 92vh);
border-radius: 16px;
box-shadow: inset 0 0 0 4px #000, 0 12px 24px rgba(0,0,0,0.7);
background: #f5f0e6;
touch-action: none;
cursor: crosshair;
}
.hud {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 8px 4px 8px;
color: #000;
font-weight: 800;
letter-spacing: 1px;
background: #f5f0e6;
border-radius: 16px 16px 0 0;
border-bottom: 4px solid #000;
}
.hud span {
background: #ff4d00;
padding: 6px 14px;
border-radius: 40px;
border: 2px solid #000;
box-shadow: 3px 3px 0 #000;
font-size: 0.9rem;
}
.hud .win-msg {
background: #00c2a8;
}
.reset-btn {
background: #ffcc00;
border: 2px solid #000;
border-radius: 40px;
padding: 6px 16px;
font-weight: 800;
box-shadow: 3px 3px 0 #000;
cursor: pointer;
transition: 0.1s linear;
font-size: 0.9rem;
}
.reset-btn:active {
transform: translate(2px, 2px);
box-shadow: 1px 1px 0 #000;
}
.controls-hint {
font-size: 0.7rem;
background: #fff;
padding: 4px 10px;
border-radius: 20px;
border: 2px solid #000;
}
</style>
</head>
<body>
<div style="display: flex; flex-direction: column;">
<div class="hud">
<span>⚡ AXIAL DRIFT</span>
<span class="win-msg" id="statusDisplay">⚪ DRIFT</span>
<button class="reset-btn" id="resetBtn">⟳ RESET</button>
</div>
<div class="game-frame">
<canvas id="mazeCanvas" width="800" height="800"></canvas>
</div>
<div style="display: flex; justify-content: space-between; padding-top: 8px; color: #000; font-weight: 600;">
<div class="controls-hint">🖱️ DRAG / 📱 TILT</div>
<div class="controls-hint" id="gravityReadout">g: 0.0, 0.0</div>
</div>
</div>
<script>
(function(){
// ---------- BAUHAUS MAZE · SINGLE FILE ----------
const canvas = document.getElementById('mazeCanvas');
const ctx = canvas.getContext('2d');
const statusEl = document.getElementById('statusDisplay');
const gravityReadout = document.getElementById('gravityReadout');
// ---------- FIXED DIMENSIONS (logical) ----------
const W = 800, H = 800;
canvas.width = W; canvas.height = H;
// ---------- MAZE GEOMETRY (Bauhaus blocks) ----------
// walls as segments: {x1,y1,x2,y2}
const wallSegments = [
// outer border (thick)
{x1:40, y1:40, x2:760, y2:40},
{x1:760, y1:40, x2:760, y2:760},
{x1:760, y1:760, x2:40, y2:760},
{x1:40, y1:760, x2:40, y2:40},
// inner blocks – geometric composition
{x1:160, y1:40, x2:160, y2:280},
{x1:160, y1:280, x2:360, y2:280},
{x1:360, y1:280, x2:360, y2:120},
{x1:280, y1:120, x2:280, y2:40},
{x1:520, y1:40, x2:520, y2:200},
{x1:520, y1:200, x2:680, y2:200},
{x1:680, y1:200, x2:680, y2:360},
{x1:680, y1:360, x2:520, y2:360},
{x1:520, y1:360, x2:520, y2:480},
{x1:200, y1:400, x2:200, y2:600},
{x1:200, y1:600, x2:400, y2:600},
{x1:400, y1:600, x2:400, y2:480},
{x1:400, y1:480, x2:280, y2:480},
{x1:280, y1:480, x2:280, y2:400},
{x1:600, y1:520, x2:600, y2:680},
{x1:600, y1:680, x2:720, y2:680},
{x1:720, y1:680, x2:720, y2:520},
{x1:720, y1:520, x2:640, y2:520},
{x1:120, y1:680, x2:120, y2:520},
{x1:120, y1:520, x2:240, y2:520},
{x1:240, y1:520, x2:240, y2:680},
// diagonal accent (trapdoor style)
{x1:480, y1:120, x2:600, y2:240},
{x1:600, y1:240, x2:480, y2:240},
{x1:480, y1:240, x2:480, y2:120},
// dynamic trapdoor (kinetic) – will move
{x1:80, y1:300, x2:80, y2:420},
{x1:80, y1:420, x2:160, y2:420},
{x1:160, y1:420, x2:160, y2:300},
// extra block
{x1:320, y1:720, x2:320, y2:640},
{x1:320, y1:640, x2:460, y2:640},
{x1:460, y1:640, x2:460, y2:720},
];
// dynamic kinetic trapdoor (moving wall) – will be updated
let kineticWalls = [
{x1:80, y1:300, x2:80, y2:420, baseX:80, offset:0, speed:0.02, axis:'y'}, // vertical wall moving horizontally
{x1:80, y1:420, x2:160, y2:420, baseY:420, offset:0, speed:0.015, axis:'x'}, // horizontal wall moving vertically
{x1:160, y1:420, x2:160, y2:300, baseX:160, offset:0, speed:0.02, axis:'y'}
];
// merge static + kinetic (kinetic will be updated)
function getAllWalls() {
let walls = wallSegments.slice();
for (let k of kineticWalls) {
walls.push({x1:k.x1, y1:k.y1, x2:k.x2, y2:k.y2});
}
return walls;
}
// ---------- MARBLE ----------
let marble = {
x: 120, y: 120, // start near top-left
vx: 0, vy: 0,
radius: 14,
friction: 0.985, // damping
angularVel: 0,
angle: 0
};
// ---------- VORTEX (goal) ----------
const vortex = { x: 700, y: 700, radius: 28 };
// ---------- GRAVITY (global vector) ----------
let gravity = { x: 0, y: 0.4 }; // initial gentle downward
let targetGravity = { x: 0, y: 0.4 };
// ---------- INPUT: mouse drag ----------
let isDragging = false;
let lastMouse = { x: 0, y: 0 };
function handleDragStart(e) {
e.preventDefault();
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
const mouseX = (e.clientX - rect.left) * scaleX;
const mouseY = (e.clientY - rect.top) * scaleY;
isDragging = true;
lastMouse = { x: mouseX, y: mouseY };
}
function handleDragMove(e) {
if (!isDragging) return;
e.preventDefault();
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
const mouseX = (e.clientX - rect.left) * scaleX;
const mouseY = (e.clientY - rect.top) * scaleY;
// delta from last position -> gravity vector (inverted: drag up -> gravity up)
const dx = mouseX - lastMouse.x;
const dy = mouseY - lastMouse.y;
// sensitivity
const sens = 0.02;
targetGravity.x = Math.max(-1, Math.min(1, targetGravity.x + dx * sens));
targetGravity.y = Math.max(-1, Math.min(1, targetGravity.y + dy * sens));
lastMouse = { x: mouseX, y: mouseY };
}
function handleDragEnd(e) {
isDragging = false;
}
// ---------- DEVICE ORIENTATION (mobile tilt) ----------
function handleOrientation(e) {
if (e.gamma === null || e.beta === null) return;
// gamma: left/right (-90 to 90), beta: front/back (-180 to 180)
let x = e.gamma / 45; // normalize
let y = e.beta / 45;
x = Math.max(-1, Math.min(1, x));
y = Math.max(-1, Math.min(1, y));
targetGravity.x = x * 0.8;
targetGravity.y = y * 0.8;
}
// ---------- COLLISION & PHYSICS ----------
function closestPointOnSegment(px, py, x1, y1, x2, y2) {
const dx = x2 - x1;
const dy = y2 - y1;
const len2 = dx*dx + dy*dy;
let t = ((px - x1) * dx + (py - y1) * dy) / len2;
t = Math.max(0, Math.min(1, t));
const projX = x1 + t * dx;
const projY = y1 + t * dy;
return { x: projX, y: projY };
}
function resolveCollisions() {
const walls = getAllWalls();
const r = marble.radius;
for (let wall of walls) {
const closest = closestPointOnSegment(marble.x, marble.y, wall.x1, wall.y1, wall.x2, wall.y2);
const distX = marble.x - closest.x;
const distY = marble.y - closest.y;
const dist = Math.sqrt(distX*distX + distY*distY);
if (dist < r) {
// push out
if (dist === 0) {
// fallback
marble.x += 0.1;
continue;
}
const nx = distX / dist;
const ny = distY / dist;
marble.x = closest.x + nx * r;
marble.y = closest.y + ny * r;
// reflect velocity (with damping)
const dot = marble.vx * nx + marble.vy * ny;
if (dot < 0) {
marble.vx -= 1.8 * dot * nx;
marble.vy -= 1.8 * dot * ny;
marble.vx *= 0.8;
marble.vy *= 0.8;
}
}
}
}
function updatePhysics() {
// smooth gravity
gravity.x += (targetGravity.x - gravity.x) * 0.08;
gravity.y += (targetGravity.y - gravity.y) * 0.08;
// apply gravity
marble.vx += gravity.x * 0.5;
marble.vy += gravity.y * 0.5;
// friction / damping
marble.vx *= marble.friction;
marble.vy *= marble.friction;
// integrate
marble.x += marble.vx;
marble.y += marble.vy;
// angular velocity (visual)
const speed = Math.sqrt(marble.vx*marble.vx + marble.vy*marble.vy);
marble.angularVel = speed * 0.02;
marble.angle += marble.angularVel;
// collision
resolveCollisions();
// boundary safety (extra)
marble.x = Math.max(marble.radius+10, Math.min(W-marble.radius-10, marble.x));
marble.y = Math.max(marble.radius+10, Math.min(H-marble.radius-10, marble.y));
// update kinetic trapdoors (dynamic)
for (let k of kineticWalls) {
if (k.axis === 'y') {
// vertical wall moves horizontally
k.offset += k.speed;
k.x1 = k.baseX + Math.sin(k.offset) * 25;
k.x2 = k.baseX + Math.sin(k.offset) * 25;
} else if (k.axis === 'x') {
// horizontal wall moves vertically
k.offset += k.speed;
k.y1 = k.baseY + Math.cos(k.offset * 0.7) * 20;
k.y2 = k.baseY + Math.cos(k.offset * 0.7) * 20;
}
}
}
// ---------- WIN CONDITION ----------
function checkWin() {
const dx = marble.x - vortex.x;
const dy = marble.y - vortex.y;
const dist = Math.sqrt(dx*dx + dy*dy);
if (dist < vortex.radius + marble.radius) {
statusEl.innerText = '🌀 CAPTURED';
statusEl.style.background = '#00c2a8';
return true;
} else {
statusEl.innerText = '⚪ DRIFT';
statusEl.style.background = '#ff4d00';
return false;
}
}
// ---------- RENDER (Bauhaus style) ----------
function draw() {
ctx.clearRect(0, 0, W, H);
// background
ctx.fillStyle = '#f5f0e6';
ctx.fillRect(0, 0, W, H);
// draw static walls (heavy black)
ctx.lineWidth = 8;
ctx.strokeStyle = '#000';
ctx.shadowColor = '#00000055';
ctx.shadowBlur = 12;
ctx.shadowOffsetX = 4;
ctx.shadowOffsetY = 4;
// static walls
for (let w of wallSegments) {
ctx.beginPath();
ctx.moveTo(w.x1, w.y1);
ctx.lineTo(w.x2, w.y2);
ctx.stroke();
}
// kinetic walls (red accent)
ctx.strokeStyle = '#ff4d00';
ctx.lineWidth = 10;
for (let k of kineticWalls) {
ctx.beginPath();
ctx.moveTo(k.x1, k.y1);
ctx.lineTo(k.x2, k.y2);
ctx.stroke();
}
// reset shadow for shapes
ctx.shadowBlur = 16;
ctx.shadowOffsetX = 6;
ctx.shadowOffsetY = 6;
// vortex (goal) – Bauhaus circle
ctx.fillStyle = '#00c2a8';
ctx.shadowColor = '#00000088';
ctx.beginPath();
ctx.arc(vortex.x, vortex.y, vortex.radius, 0, Math.PI*2);
ctx.fill();
ctx.strokeStyle = '#000';
ctx.lineWidth = 5;
ctx.stroke();
// inner vortex
ctx.fillStyle = '#ffcc00';
ctx.beginPath();
ctx.arc(vortex.x, vortex.y, vortex.radius*0.5, 0, Math.PI*2);
ctx.fill();
ctx.stroke();
// marble (red with shadow)
ctx.shadowColor = '#000000aa';
ctx.fillStyle = '#ff4d00';
ctx.beginPath();
ctx.arc(marble.x, marble.y, marble.radius, 0, Math.PI*2);
ctx.fill();
ctx.strokeStyle = '#000';
ctx.lineWidth = 4;
ctx.stroke();
// marble highlight
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.fillStyle = '#ffffffcc';
ctx.beginPath();
ctx.arc(marble.x-3, marble.y-3, 4, 0, Math.PI*2);
ctx.fill();
// gravity vector indicator (arrow)
ctx.shadowBlur = 8;
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.strokeStyle = '#000';
ctx.lineWidth = 4;
const gx = marble.x + gravity.x * 60;
const gy = marble.y + gravity.y * 60;
ctx.beginPath();
ctx.moveTo(marble.x, marble.y);
ctx.lineTo(gx, gy);
ctx.stroke();
// arrowhead
const angle = Math.atan2(gy - marble.y, gx - marble.x);
ctx.fillStyle = '#000';
ctx.beginPath();
ctx.moveTo(gx, gy);
ctx.lineTo(gx - 12*Math.cos(angle-0.4), gy - 12*Math.sin(angle-0.4));
ctx.lineTo(gx - 12*Math.cos(angle+0.4), gy - 12*Math.sin(angle+0.4));
ctx.fill();
// reset shadow
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
// update HUD gravity
gravityReadout.innerText = `g: ${gravity.x.toFixed(2)}, ${gravity.y.toFixed(2)}`;
}
// ---------- GAME LOOP ----------
function gameLoop() {
updatePhysics();
checkWin();
draw();
requestAnimationFrame(gameLoop);
}
// ---------- RESET ----------
function resetGame() {
marble.x = 120; marble.y = 120;
marble.vx = 0; marble.vy = 0;
targetGravity = { x: 0, y: 0.4 };
gravity = { x: 0, y: 0.4 };
statusEl.innerText = '⚪ DRIFT';
statusEl.style.background = '#ff4d00';
// reset kinetic offsets
for (let k of kineticWalls) {
k.offset = 0;
if (k.axis === 'y') {
k.x1 = k.baseX; k.x2 = k.baseX;
} else {
k.y1 = k.baseY; k.y2 = k.baseY;
}
}
}
// ---------- EVENT LISTENERS ----------
canvas.addEventListener('mousedown', handleDragStart);
window.addEventListener('mousemove', handleDragMove);
window.addEventListener('mouseup', handleDragEnd);
canvas.addEventListener('touchstart', (e) => {
const touch = e.touches[0];
const mouseEvent = new MouseEvent('mousedown', {
clientX: touch.clientX,
clientY: touch.clientY
});
canvas.dispatchEvent(mouseEvent);
e.preventDefault();
}, { passive: false });
canvas.addEventListener('touchmove', (e) => {
const touch = e.touches[0];
const mouseEvent = new MouseEvent('mousemove', {
clientX: touch.clientX,
clientY: touch.clientY
});
window.dispatchEvent(mouseEvent);
e.preventDefault();
}, { passive: false });
canvas.addEventListener('touchend', (e) => {
window.dispatchEvent(new MouseEvent('mouseup'));
e.preventDefault();
}, { passive: false });
// device orientation
if (window.DeviceOrientationEvent) {
window.addEventListener('deviceorientation', handleOrientation, true);
}
document.getElementById('resetBtn').addEventListener('click', resetGame);
// start
resetGame();
gameLoop();
})();
</script>
</body>
</html>
用法
此提示词专为 coding 设计。复制上方内容并粘贴到你常用的 AI 工具中。
为获得最佳效果,可将占位符(方括号或大写字母标示)替换为你的具体需求。
讨论
0 条评论