// 摄像机:跟随 / 锁定 / 缩放(坑向关的"摄像头"道具靠它搞事) 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); } }