agent-Specialization/test/snake.html

283 lines
10 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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 {
width: 100vw;
height: 100vh;
background: linear-gradient(135deg, #0a0a0a, #1a1a1a, #2d2d2d);
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
}
#snake-canvas {
display: block;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<canvas id="snake-canvas"></canvas>
<script>
const canvas = document.getElementById('snake-canvas');
const ctx = canvas.getContext('2d');
// 设置canvas尺寸为窗口大小
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
class Snake {
constructor() {
this.radius = 14; // 蛇的半径苹果也是14
this.path = [
{ x: canvas.width / 2, y: canvas.height / 2 }
];
this.targetLength = 28; // 初始长度为一个苹果直径
this.currentLength = 28;
this.speed = 2;
this.angle = 0;
this.targetAngle = 0;
this.hue = 30; // 橙黄色
this.currentTarget = null; // 当前追逐的苹果
this.targetStartTime = Date.now(); // 开始追逐当前目标的时间
this.targetTimeout = 10000; // 10秒超时
this.timedOut = false; // 是否刚刚超时
}
findNearestApple() {
const now = Date.now();
// 如果当前目标存在且未超时,继续追逐
if (this.currentTarget && (now - this.targetStartTime) < this.targetTimeout) {
// 检查当前目标是否还在苹果列表中
const targetStillExists = apples.some(apple =>
apple.x === this.currentTarget.x && apple.y === this.currentTarget.y
);
if (targetStillExists) {
const dx = this.currentTarget.x - this.path[0].x;
const dy = this.currentTarget.y - this.path[0].y;
this.targetAngle = Math.atan2(dy, dx);
this.timedOut = false;
return;
}
}
// 超时或目标不存在,寻找新目标
let targetApple = null;
let targetDistance = this.timedOut ? -Infinity : Infinity; // 超时后找最远的
apples.forEach(apple => {
const dx = apple.x - this.path[0].x;
const dy = apple.y - this.path[0].y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (this.timedOut) {
// 超时了,找最远的苹果
if (distance > targetDistance) {
targetDistance = distance;
targetApple = apple;
}
} else {
// 正常情况,找最近的苹果
if (distance < targetDistance) {
targetDistance = distance;
targetApple = apple;
}
}
});
if (targetApple) {
// 如果切换了目标,重置计时器
if (!this.currentTarget ||
this.currentTarget.x !== targetApple.x ||
this.currentTarget.y !== targetApple.y) {
this.currentTarget = targetApple;
this.targetStartTime = now;
this.timedOut = false; // 重置超时标志
} else if ((now - this.targetStartTime) >= this.targetTimeout) {
// 刚好达到超时
this.timedOut = true;
}
const dx = targetApple.x - this.path[0].x;
const dy = targetApple.y - this.path[0].y;
this.targetAngle = Math.atan2(dy, dx);
}
}
update() {
// 平滑转向 - 降低转向速度,避免锐角转弯
let angleDiff = this.targetAngle - this.angle;
while (angleDiff > Math.PI) angleDiff -= 2 * Math.PI;
while (angleDiff < -Math.PI) angleDiff += 2 * Math.PI;
// 限制每帧最大转向角度
const maxTurnRate = 0.03; // 降低转向速度
this.angle += angleDiff * maxTurnRate;
// 移动头部
const head = { ...this.path[0] };
head.x += Math.cos(this.angle) * this.speed;
head.y += Math.sin(this.angle) * this.speed;
// 屏幕边界穿越
if (head.x < 0) head.x = canvas.width;
if (head.x > canvas.width) head.x = 0;
if (head.y < 0) head.y = canvas.height;
if (head.y > canvas.height) head.y = 0;
// 添加新头部
this.path.unshift(head);
// 检查是否吃到苹果
apples.forEach((apple, index) => {
const dx = head.x - apple.x;
const dy = head.y - apple.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < this.radius + 14) { // 14是苹果半径
apples[index] = createApple();
this.targetLength += 28; // 增加一个苹果直径的长度
// 吃到苹果后清除当前目标,重新寻找
this.currentTarget = null;
}
});
// 计算当前路径实际长度
let pathLength = 0;
for (let i = 0; i < this.path.length - 1; i++) {
const dx = this.path[i].x - this.path[i + 1].x;
const dy = this.path[i].y - this.path[i + 1].y;
pathLength += Math.sqrt(dx * dx + dy * dy);
}
// 从尾部裁剪多余的路径
while (this.path.length > 2 && pathLength > this.targetLength) {
const last = this.path[this.path.length - 1];
const secondLast = this.path[this.path.length - 2];
const dx = secondLast.x - last.x;
const dy = secondLast.y - last.y;
const segmentLength = Math.sqrt(dx * dx + dy * dy);
if (pathLength - segmentLength >= this.targetLength) {
this.path.pop();
pathLength -= segmentLength;
} else {
break;
}
}
this.currentLength = pathLength;
}
draw() {
if (this.path.length < 2) return;
// 绘制丝带状的蛇身
ctx.strokeStyle = `hsl(${this.hue}, 70%, 65%)`;
ctx.lineWidth = this.radius * 2;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.shadowBlur = 20;
ctx.shadowColor = `hsl(${this.hue}, 80%, 55%)`;
ctx.beginPath();
ctx.moveTo(this.path[0].x, this.path[0].y);
for (let i = 1; i < this.path.length; i++) {
ctx.lineTo(this.path[i].x, this.path[i].y);
}
ctx.stroke();
// 重置阴影
ctx.shadowBlur = 0;
}
}
const apples = [];
function createApple() {
const margin = 50;
return {
x: margin + Math.random() * (canvas.width - margin * 2),
y: margin + Math.random() * (canvas.height - margin * 2)
};
}
// 初始化3个苹果
for (let i = 0; i < 3; i++) {
apples.push(createApple());
}
function drawApples() {
apples.forEach(apple => {
// 绘制光晕
const gradient = ctx.createRadialGradient(
apple.x, apple.y, 0,
apple.x, apple.y, 28
);
gradient.addColorStop(0, `hsla(${snake.hue}, 70%, 65%, 0.5)`);
gradient.addColorStop(1, 'transparent');
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(apple.x, apple.y, 28, 0, Math.PI * 2);
ctx.fill();
// 绘制苹果本体
ctx.fillStyle = `hsl(${snake.hue}, 70%, 65%)`;
ctx.beginPath();
ctx.arc(apple.x, apple.y, 14, 0, Math.PI * 2);
ctx.fill();
});
}
const snake = new Snake();
let lastTime = 0;
const FPS = 60;
const frameInterval = 1000 / FPS;
function animate(currentTime) {
requestAnimationFrame(animate);
// 限制帧率为60FPS
const elapsed = currentTime - lastTime;
if (elapsed < frameInterval) {
return;
}
lastTime = currentTime - (elapsed % frameInterval);
ctx.clearRect(0, 0, canvas.width, canvas.height);
snake.findNearestApple();
snake.update();
drawApples();
snake.draw();
}
// 开始动画
animate(0);
</script>
</body>
</html>