示例成品AI 按一组示范选项写的
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>物理沙盒小游戏</title>
<style>
body { margin:0; display:flex; height:100vh; background:#1a1a2e; color:#fff; font-family:sans-serif; }
canvas { flex:1; cursor:crosshair; }
.panel { width:140px; padding:15px; background:#16213e; display:flex; flex-direction:column; gap:12px; }
.panel h3 { margin:0 0 5px; font-size:14px; color:#e94560; }
button { padding:8px; border:none; border-radius:6px; background:#0f3460; color:#fff; cursor:pointer; font-size:13px; }
button:hover { background:#e94560; }
p { font-size:12px; color:#aaa; margin:0; line-height:1.4; }
</style>
</head>
<body>
<div class="panel">
<h3>🧊 物理沙盒</h3>
<p>点击画布投放积木</p>
<button onclick="clearAll()">清空画布</button>
<button onclick="toggleShape()">切换形状: 圆形</button>
<p>作者: 张爸爸</p>
<p>给5岁儿子做的重力小游戏</p>
</div>
<canvas id="c"></canvas>
<script>
// 简易物理引擎核心:重力、碰撞、弹跳
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
let W, H;
const G = 0.5, BOUNCE = 0.6, FRICTION = 0.98;
let bodies = [], isCircle = true;
function resize() { W = canvas.width = canvas.clientWidth; H = canvas.height = canvas.clientHeight; }
window.addEventListener('resize', resize); resize();
// 物理对象
class Body {
constructor(x, y, circle) {
this.x = x; this.y = y; this.vx = 0; this.vy = 0;
this.r = 15 + Math.random() * 15; // 半径随机
this.w = this.r * 2; this.h = this.r * 2;
this.circle = circle;
this.color = `hsl(${Math.random()*360},70%,60%)`;
}
update() {
this.vy += G; // 施加重力
this.vx *= FRICTION;
this.x += this.vx; this.y += this.vy;
// 地板与墙壁碰撞
if (this.y + this.r > H) { this.y = H - this.r; this.vy *= -BOUNCE; }
if (this.x - this.r < 0) { this.x = this.r; this.vx *= -BOUNCE; }
if (this.x + this.r > W) { this.x = W - this.r; this.vx *= -BOUNCE; }
}
draw() {
ctx.fillStyle = this.color;
if (this.circle) {
ctx.beginPath(); ctx.arc(this.x, this.y, this.r, 0, Math.PI*2); ctx.fill();
} else {
ctx.fillRect(this.x - this.r, this.y - this.r, this.w, this.h);
}
}
}
// 简易物体间碰撞(圆形近似)
function collide(a, b) {
let dx = b.x - a.x, dy = b.y - a.y, dist = Math.sqrt(dx*dx + dy*dy);
let minDist = a.r + b.r;
if (dist < minDist && dist > 0) {
let nx = dx/dist, ny = dy/dist, overlap = minDist - dist;
a.x -= nx * overlap/2; a.y -= ny * overlap/2;
b.x += nx * overlap/2; b.y += ny * overlap/2;
let dvx = a.vx - b.vx, dvy = a.vy - b.vy, dvn = dvx*nx + dvy*ny;
if (dvn > 0) { a.vx -= dvn*nx*BOUNCE; a.vy -= dvn*ny*BOUNCE; b.vx += dvn*nx*BOUNCE; b.vy += dvn*ny*BOUNCE; }
}
}
canvas.addEventListener('click', e => {
let rect = canvas.getBoundingClientRect();
bodies.push(new Body(e.clientX - rect.left, e.clientY - rect.top, isCircle));
if (bodies.length > 150) bodies.shift(); // 防卡顿,限制数量
});
function clearAll() { bodies = []; }
function toggleShape() { isCircle = !isCircle; document.querySelector('.panel button:nth-child(4)').textContent = `切换形状: ${isCircle?'圆形':'方块'}`; }
function loop() {
ctx.clearRect(0, 0, W, H);
for (let i=0; i<bodies.length; i++) {
bodies[i].update();
for (let j=i+1; j<bodies.length; j++) collide(bodies[i], bodies[j]);
}
bodies.forEach(b => b.draw());
requestAnimationFrame(loop);
}
loop();
</script>
</body>
</html>
```