Spaces:
Running
Running
File size: 1,925 Bytes
71eae78 2428d78 71eae78 2428d78 71eae78 2428d78 71eae78 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | // Follows the prince horizontally with a dead zone across the middle third.
// No vertical movement. No easing, no overshoot. Spec 2.2: "It should be
// boring."
//
// This is the only place world coordinates become screen coordinates.
import { TUNING } from './tuning.js';
export function createCamera(viewportW, worldMin, worldMax) {
const halfDead = (viewportW * TUNING.CAMERA_DEAD_ZONE) / 2;
// Mutable, because the village changes them exactly once. On the road the
// camera follows forever; at the mouth it stops following and starts being
// bounded, and that one switch IS the transition between the two modes.
let minX = worldMin;
let maxX = Math.max(worldMin, worldMax - viewportW);
return {
x: worldMin, prevX: worldMin,
snapshot() { this.prevX = this.x; },
reset(targetX) {
this.x = this.prevX = this.clamp(targetX - viewportW / 2);
},
/**
* Re-bound the view. Takes CAMERA positions, not world edges — the mouth of
* the village fixes where the view may go, not where the world stops, and
* passing world edges through the viewport subtraction twice is exactly the
* off-by-a-half-screen this file has already produced once.
*/
setBounds(minCamX, maxCamX) {
minX = minCamX;
maxX = Math.max(minCamX, maxCamX);
this.x = this.clamp(this.x);
},
clamp(x) {
return Math.min(maxX, Math.max(minX, x));
},
/** Move only when the target leaves the middle third. */
follow(targetX) {
const centre = this.x + viewportW / 2;
const offset = targetX - centre;
if (offset > halfDead) this.x += offset - halfDead;
else if (offset < -halfDead) this.x += offset + halfDead;
this.x = this.clamp(this.x);
},
/** World x -> screen x at a given parallax rate. 1.0 is the actor layer. */
toScreen(worldX, rate = 1) {
return worldX - this.x * rate;
},
};
}
|