feat: 新增多项功能并添加新工具
- 私人剪贴板新增限时直通URL生成功能 - doc_cloud_keeper新增标题复制按钮 - 新增贪蛇出动游戏工具 - 新增OSS中转直链工具 - 添加批量抽帧测试脚本
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
/* 贪蛇出动 - AI 蛇控制器:觅食/避障/边界回避/随机加速 */
|
||||
(function () {
|
||||
'use strict';
|
||||
window.GS = window.GS || {};
|
||||
const U = GS.U;
|
||||
|
||||
class AI {
|
||||
constructor(snake) {
|
||||
this.s = snake;
|
||||
this.decideTimer = U.rand(0, 0.3);
|
||||
this.wanderAngle = snake.angle;
|
||||
this.boostTimer = 0;
|
||||
}
|
||||
|
||||
update(dt, world, snakes) {
|
||||
const s = this.s;
|
||||
if (s.dead) return;
|
||||
this.decideTimer -= dt;
|
||||
this.boostTimer -= dt;
|
||||
if (this.decideTimer > 0) {
|
||||
s.boosting = this.boostTimer > 0;
|
||||
return;
|
||||
}
|
||||
this.decideTimer = 0.15;
|
||||
|
||||
const h = s.head;
|
||||
const look = 90 + s.radius * 3;
|
||||
const aheadX = h.x + Math.cos(s.angle) * look;
|
||||
const aheadY = h.y + Math.sin(s.angle) * look;
|
||||
|
||||
// 1) 边界回避
|
||||
const margin = 220;
|
||||
let avoid = null;
|
||||
if (aheadX < margin || aheadY < margin || aheadX > world.size - margin || aheadY > world.size - margin) {
|
||||
avoid = Math.atan2(world.size / 2 - h.y, world.size / 2 - h.x);
|
||||
}
|
||||
|
||||
// 2) 蛇身回避:检查前方采样点是否撞上其他蛇
|
||||
if (avoid == null) {
|
||||
for (const o of snakes) {
|
||||
if (o === s || o.dead) continue;
|
||||
const segs = o.segments();
|
||||
const rr = (s.radius + o.radius + 26);
|
||||
for (let i = 0; i < segs.length; i += 2) {
|
||||
if (U.dist2(aheadX, aheadY, segs[i].x, segs[i].y) < rr * rr) {
|
||||
// 向左或向右转,选更开阔的一侧
|
||||
const left = s.angle - Math.PI / 2, right = s.angle + Math.PI / 2;
|
||||
avoid = this._clearer(left, right, h, snakes, s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (avoid != null) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (avoid != null) {
|
||||
s.targetAngle = avoid;
|
||||
s.boosting = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// 3) 觅食:找最近金币
|
||||
let best = null, bestD = 520 * 520;
|
||||
const near = world.grid.query(h.x, h.y, 520);
|
||||
for (const c of near) {
|
||||
const d2 = U.dist2(h.x, h.y, c.x, c.y);
|
||||
if (d2 < bestD) { bestD = d2; best = c; }
|
||||
}
|
||||
if (best) {
|
||||
s.targetAngle = Math.atan2(best.y - h.y, best.x - h.x);
|
||||
} else {
|
||||
// 漫游:小幅随机偏转
|
||||
this.wanderAngle += U.rand(-0.6, 0.6);
|
||||
s.targetAngle = this.wanderAngle;
|
||||
}
|
||||
|
||||
// 4) 随机加速:追食或逃跑时小概率冲刺
|
||||
if (Math.random() < 0.06 && s.score > 30) this.boostTimer = U.rand(0.4, 1.0);
|
||||
s.boosting = this.boostTimer > 0;
|
||||
}
|
||||
|
||||
_clearer(a1, a2, h, snakes, s) {
|
||||
const score = a => {
|
||||
const px = h.x + Math.cos(a) * 160, py = h.y + Math.sin(a) * 160;
|
||||
let d = 1e9;
|
||||
for (const o of snakes) {
|
||||
if (o === s || o.dead) continue;
|
||||
const segs = o.segments();
|
||||
for (let i = 0; i < segs.length; i += 3) {
|
||||
const dd = U.dist2(px, py, segs[i].x, segs[i].y);
|
||||
if (dd < d) d = dd;
|
||||
}
|
||||
}
|
||||
return d;
|
||||
};
|
||||
return score(a1) >= score(a2) ? a1 : a2;
|
||||
}
|
||||
}
|
||||
|
||||
GS.AI = AI;
|
||||
})();
|
||||
Reference in New Issue
Block a user