91 lines
2.6 KiB
JavaScript
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);
|
|
}
|
|
}
|