gibbon.js
Version:
Actor/Component system for use with pixi.js.
219 lines (159 loc) • 5.01 kB
text/typescript
import type { DisplayObject } from 'pixi.js';
import { Point, Rectangle } from 'pixi.js';
import { Component } from '../core/component';
import type { IPoint } from '../data/geom';
import { EngineEvent } from '../events/engine-events';
export class Camera extends Component<DisplayObject> {
/**
* Target for camera to follow.
*/
get target() { return this._target; }
set target(v) {
if (v) {
this._target = v;
this._viewRect.x = v.x - this._halfWidth;
this._viewRect.y = v.y - this._halfHeight;
this._panClip?.position.set(this._halfWidth - v.x, this._halfHeight - v.y);
}
}
/**
* Set the clip that moves to change the camera's position.
* This should be a clip that contains all game objects.
* (As opposed to static UI elements, backgrounds, etc.)
*/
get panClip() { return this._panClip }
set panClip(v: DisplayObject | null | undefined) {
this._panClip = v;
}
get minScale(): number { return this._minScale || 1; }
set minScale(v: number) { this._minScale = v; }
get maxScale(): number { return this._maxScale || 1; }
set maxScale(v: number) { this._maxScale = v; }
get viewScale(): number { return this._viewScale; }
set viewScale(v: number) {
this._viewScale = v;
this._panClip?.scale.set(v, v);
}
get x(): number { return -(this._panClip?.x ?? 0); }
set x(v: number) {
this._viewRect.x = v * this._viewScale;
if (this._panClip != null) {
this._panClip.x = -this._viewRect.x;
}
}
get y(): number { return -(this._panClip?.y ?? 0); }
set y(v: number) {
this._viewRect.y = v * this._viewScale;
if (this._panClip != null) {
this._panClip.y = -this._viewRect.y;
}
}
/**
* @property {Rectangle} Visible rectangle in the Camera's coordinate system.
*/
get viewRect() { return this._viewRect; }
get centerX() { return this._viewRect.x + this._halfWidth; }
get centerY() { return this._viewRect.y + this._halfHeight; }
get center(): IPoint {
return new Point(
this._viewRect.x + this._halfWidth,
this._viewRect.y + this._halfHeight);
}
get left(): number { return this._viewRect.left; }
get right(): number { return this._viewRect.right; }
get top(): number { return this._viewRect.top; }
get bottom(): number { return this._viewRect.bottom; }
/**
* Target camera should track.
*/
private _target: { position: IPoint, x: number, y: number } | null = null;
private _minScale: number = 0;
private _maxScale: number = 0;
private _viewScale: number = 0;
private readonly _viewRect: Rectangle = new Rectangle();
private _panClip?: DisplayObject | null;
private _halfWidth: number = 0;
private _halfHeight: number = 0;
constructor(rect: Rectangle) {
super();
this.resized(rect);
}
init() {
this._target = null;
this._panClip = this.actor!.clip;
this._viewScale = 1;
this._halfWidth = this._viewRect.width / 2;
this._halfHeight = this._viewRect.height / 2;
this.game!.on(EngineEvent.ScreenResized, this.resized, this);
}
/**
* Call when view size has changed.
* @param rect
*/
resized(rect: { x: number, y: number, width: number, height: number }) {
this._viewRect.width = rect.width;
this._viewRect.height = rect.height;
this._halfWidth = rect.width / 2;
this._halfHeight = rect.height / 2;
}
/**
* Determines if an item is completely within the view.
* @param {*} it
* @returns true if item is completely onscreen, false otherwise.
*/
containsItem(it: any): boolean {
throw Error("Not implemented.");
}
/**
*
* @param {*} it
* @returns true if item is within the camera view, false otherwise.
*/
itemInView(it: any): boolean {
throw Error("Not implemented.");
}
/**
*
* @param p
*/
ptInView(p: IPoint): Boolean {
return this._viewRect?.contains(p.x, p.y) ?? false;
}
/**
*
* @param {Rectangle} r
* @returns true if a rectangle falls within the camera view, false otherwise.
*/
rectInView(r: Rectangle) {
return r.x < this._viewRect.right &&
r.right > this._viewRect.x &&
r.y < this._viewRect.bottom &&
r.bottom > this._viewRect.y;
}
/**
*
* @param global
* @param dest=null
* @returns {Point}
*/
toCameraPoint(global: IPoint, dest?: IPoint) {
return this._panClip?.toLocal(global, undefined, dest ?? new Point());
}
update() {
if (this._target === null) return;
const targPos = this._target.position;
const destX = this._halfWidth - targPos.x;
const destY = this._halfHeight - targPos.y;
const pos = this._panClip!.position;
pos.set(
pos.x + (destX - pos.x) / 4,
pos.y + (destY - pos.y) / 4
);
//console.log('cam pos: ' + pos.x + ', '+ pos.y );
this._viewRect.x = -pos.x;
this._viewRect.y = -pos.y;
}
onDestroy() {
this.game?.off(EngineEvent.ScreenResized, this.resized, this);
}
}