Files
kidgo/js/camera.js
T
2026-10-20 22:21:26 +08:00

91 lines
2.6 KiB
JavaScript

// 摄像机:跟随 / 锁定 / 缩放(坑向关的"摄像头"道具靠它搞事)
import { clamp, lerp } from './util.js';
export class Camera {
constructor(w = 800, h = 608) {
this.w = w; this.h = h;
this.x = 0; this.y = 0;
this.zoom = 1;
this.tz = 1;
this.mode = 'follow';
this.lockX = 0; this.lockY = 0;
this.shakeX = 0; this.shakeY = 0;
this.snapped = false;
}
reset(level, player) {
this.zoom = 1; this.tz = 1; this.mode = 'follow';
this.snapTo(player, level);
}
viewW() { return this.w / this.zoom; }
viewH() { return this.h / this.zoom; }
snapTo(player, level) {
this.x = clamp(player.cx - this.viewW() / 2, 0, Math.max(0, level.pxW - this.viewW()));
this.y = clamp(player.cy - this.viewH() / 2, 0, Math.max(0, level.pxH - this.viewH()));
this.mode = 'follow';
this.zoom = 1; this.tz = 1;
}
update(level, player, dt) {
const zone = level.camZone;
let tx, ty, tz = 1, speed = 0.16;
if (zone) {
if (zone.mode === 'lock') {
this.mode = 'lock';
tx = zone.lx !== null ? zone.lx : zone.x;
ty = zone.ly !== null ? zone.ly : zone.y;
tz = zone.zoom || 1;
speed = 0.05; // 缓慢移动,制造迷失感
} else if (zone.mode === 'zoom') {
this.mode = 'zoom';
tz = zone.zoom || 1;
tx = player.cx - this.w / tz / 2;
ty = player.cy - this.h / tz / 2;
speed = 0.14;
} else if (zone.mode === 'off') {
this.mode = 'off';
tx = this.x; ty = this.y; tz = zone.zoom || this.zoom; speed = 0;
}
} else {
this.mode = 'follow';
tx = player.cx - this.w / this.zoom / 2;
ty = player.cy - this.h / this.zoom / 2;
tz = 1;
speed = 0.16;
}
this.tz = lerp(this.tz, tz, 0.06);
if (Math.abs(this.tz - this.zoom) > 0.002) this.zoom = this.tz;
if (speed > 0) {
this.x = lerp(this.x, tx, speed);
this.y = lerp(this.y, ty, speed);
}
this.clampToLevel(level);
// 抖动
const sh = level.shake || 0;
if (sh > 0.2) {
this.shakeX = (Math.random() - 0.5) * sh;
this.shakeY = (Math.random() - 0.5) * sh;
} else { this.shakeX = 0; this.shakeY = 0; }
}
clampToLevel(level) {
const vw = this.viewW(), vh = this.viewH();
if (level.pxW <= vw) { this.x = (level.pxW - vw) / 2; }
else this.x = clamp(this.x, 0, level.pxW - vw);
if (level.pxH <= vh) { this.y = (level.pxH - vh) / 2; }
else this.y = clamp(this.y, 0, level.pxH - vh);
}
apply(ctx) {
ctx.translate(this.shakeX, this.shakeY);
if (this.zoom !== 1) ctx.scale(this.zoom, this.zoom);
ctx.translate(-this.x, -this.y);
}
}