@litecanvas/utils
Version:
Utilities to help build litecanvas games
212 lines (209 loc) • 5.35 kB
JavaScript
(() => {
// src/_global.js
globalThis.utils = globalThis.utils || {};
globalThis.utils.global = (overrides = true) => {
for (const key in globalThis.utils) {
if ("global" === key) continue;
if (overrides || globalThis[key] === void 0) {
globalThis[key] = globalThis.utils[key];
}
}
};
// src/camera/index.js
var Camera = class {
/** @type {LitecanvasInstance} */
_engine = null;
/** @type {number} camera center X */
x = 0;
/** @type {number} camera center Y */
y = 0;
/** @type {number} offset X */
ox = 0;
/** @type {number} offset Y*/
oy = 0;
/** @type {number} */
width = 0;
/** @type {number} */
height = 0;
/** @type {number} */
rotation = 0;
/** @type {number} */
scale = 1;
_shake = {
x: 0,
y: 0,
removeListener: null
};
/**
* @param {LitecanvasInstance} engine
*/
constructor(engine = null, ox = 0, oy = 0, width = null, height = null) {
this._engine = engine || globalThis;
this.ox = ox;
this.oy = oy;
this.resize(
width || this._engine.WIDTH - ox,
height || this._engine.HEIGHT - oy
);
this.x = this.width / 2;
this.y = this.height / 2;
}
resize(width, height) {
this.width = width;
this.height = height;
this._engine.emit("camera-resized", this);
}
/**
* @param {boolean} [clip] default: `false`
*/
start(clip = false) {
this._engine.push();
if (clip) {
const region = path();
region.rect(this.ox, this.oy, this.width, this.height);
this._engine.clip(region);
}
const centerX = this.ox + this.width / 2, centerY = this.oy + this.height / 2;
this._engine.translate(centerX, centerY);
this._engine.scale(this.scale);
this._engine.rotate(this.rotation);
this._engine.translate(-this.x + this._shake.x, -this.y + this._shake.y);
}
end() {
this._engine.pop();
}
/**
* @param {number} x
* @param {number} y
*/
lookAt(x, y) {
this.x = x;
this.y = y;
}
/**
* @param {number} dx
* @param {number} dy
*/
move(dx, dy) {
this.x += dx;
this.y += dy;
}
/**
* @param {number} value
*/
zoom(value) {
this.scale *= value;
}
/**
* @param {number} value
*/
zoomTo(value) {
this.scale = value;
}
/**
* @param {number} radians
*/
rotate(radians) {
this.rotation += radians;
}
/**
* @param {number} radians
*/
rotateTo(radians) {
this.rotation = radians;
}
/**
* @param {number} x
* @param {number} y
* @param {{x: number, y: number}} [output]
* @returns {{x: number, y: number}}
*/
getWorldPoint(x, y, output = {}) {
const c = Math.cos(-this.rotation), s = Math.sin(-this.rotation);
x = (x - this.width / 2 - this.ox) / this.scale;
y = (y - this.height / 2 - this.oy) / this.scale;
output.x = c * x - s * y + this.x;
output.y = s * x + c * y + this.y;
return output;
}
/**
* @param {number} x
* @param {number} y
* @param {{x: number, y: number}} [output]
* @returns {{x: number, y: number}}
*/
getCameraPoint(x, y, output = {}) {
const c = Math.cos(-this.rotation), s = Math.sin(-this.rotation);
x = x - this.x;
y = y - this.y;
x = c * x - s * y;
y = s * x + c * y;
output.x = x * this.scale + this.width / 2 + this.ox;
output.y = y * this.scale + this.height / 2 + this.oy;
return output;
}
/**
* @returns {number[]}
*/
getBounds() {
return [this.ox, this.oy, this.width, this.height];
}
/**
* Check if a rect is inside of the camera.
*
* @param {number} x
* @param {number} y
* @param {number} width
* @param {number} height
* @returns {boolean}
*/
viewing(x, y, width, height) {
const cameraX = this.width / 2 - this.x;
const cameraY = this.height / 2 - this.y;
const cameraWidth = this.width / this.scale;
const cameraHeight = this.height / this.scale;
return this._engine.colrect(
x,
y,
width,
height,
cameraX,
cameraY,
cameraWidth,
cameraHeight
);
}
/**
* Shake the camera
*
* @param {number} amplitude
* @param {number} duration in seconds
*/
shake(amplitude = 1, duration = 0.3) {
if (this.shaking) return;
this._shake.removeListener = this._engine.listen("update", (dt) => {
this._shake.x = this._engine.randi(-amplitude, amplitude);
this._shake.y = this._engine.randi(-amplitude, amplitude);
duration -= dt;
if (duration <= 0) {
this.unshake();
}
});
}
unshake() {
if (this.shaking) {
this._shake.removeListener();
this._shake.removeListener = null;
this._shake.x = this._shake.y = 0;
}
}
/**
* @returns {boolean}
*/
get shaking() {
return this._shake.removeListener !== null;
}
};
// src/camera/_web.js
globalThis.utils = Object.assign(globalThis.utils || {}, { Camera });
})();