UNPKG

gibbon.js

Version:

Actor/Component system for use with pixi.js.

258 lines (199 loc) 6.6 kB
import { GridCell } from './grid-cell'; import { GridRange } from './grid-range'; /** * Tyoe for objects stored in Grid. */ export type Positionable = { x: number, y: number, width: number, height: number, flags?: number, hitFlags?: number }; /** * Grid for tracking Actor location and hit tests. */ export class Grid<T extends Positionable> { private rows: number; private cols: number; private _cells: GridCell<T>[][]; private width: number; private height: number; private cellWidth: number; private cellHeight: number; constructor(props: { /** * Width of entire grid. */ width: number, /** * height of entire grid. */ height: number, rows?: number, cols?: number } ) { this.width = props.width; this.height = props.height; this.rows = props.rows ?? 20; this.cols = props.cols ?? 20; this.cellWidth = this.width / this.cols; this.cellHeight = this.height / this.rows; this._cells = this.initTiles(); } /** * Add items to range of grid cells. * @param range * @param item */ public addToRange(range: GridRange, item: T) { const maxR = range.maxRow; const maxC = range.maxCol; for (let r = range.minRow; r <= maxR; r++) { for (let c = range.minCol; c <= maxC; c++) { this._cells[r][c].addItem(item); } } } public removeFromRange(range: GridRange, item: T) { const maxR = range.maxRow; const maxC = range.maxCol; for (let r = range.minRow; r <= maxR; r++) { for (let c = range.minCol; c <= maxC; c++) { this._cells[r][c].removeItem(item); } } } /** * Get hits matching item. * @param results */ public getHits(item: T, results: T[] = [], range?: GridRange) { range = range ?? this.getRange(item); for (let r = range.minRow; r <= range.minRow; r++) { for (let c = range.minCol; c <= range.maxCol; c++) { this._cells[r][c].getHits(item, results); } } return results; } /** * Get the range of an item. * @param item * @param result - Optional grid range in which to place results. */ public getRange(item: T, result?: GridRange) { result = result ?? new GridRange(); let v = Math.floor((item.y - item.height / 2) / this.cellHeight); if (v < 0) { v = 0; } else if (v >= this.rows) { v = this.rows - 1; } result.minRow = v; v = Math.floor((item.y + item.height / 2) / this.cellHeight); if (v < 0) { v = 0; } else if (v >= this.rows) { v = this.rows - 1; } result.maxRow = v; v = Math.floor((item.x - item.width / 2) / this.cellWidth); if (v < 0) { v = 0; } else if (v >= this.cols) { v = this.cols - 1; } result.minCol = v; v = Math.floor((item.x + item.width / 2) / this.cellWidth); if (v < 0) { v = 0; } else if (v >= this.cols) { v = this.cols - 1; } result.maxCol = v; return result; } /** * Get objects within grid range. * @param range * @param results */ public getItems(range: GridRange, results: T[] = []) { const maxR = range.maxRow; const maxC = range.maxCol; for (let r = range.minRow; r <= maxR; r++) { for (let c = range.minCol; c <= maxC; c++) { this._cells[r][c].getItems(results); } } return results; } public addItem(item: T) { let minRow = Math.floor((item.y - item.height / 2) / this.cellHeight); if (minRow < 0) { minRow = 0; } else if (minRow >= this.rows) { minRow = this.rows - 1; } let maxRow = Math.floor((item.y + item.height / 2) / this.cellHeight); if (maxRow < 0) { maxRow = 0; } else if (maxRow >= this.rows) { maxRow = this.rows - 1; } let minCol = Math.floor((item.x - item.width / 2) / this.cellWidth); if (minCol < 0) { minCol = 0; } else if (minCol >= this.cols) { minCol = this.cols - 1; } let maxCol = Math.floor((item.x + item.width / 2) / this.cellWidth); if (maxCol < 0) { maxCol = 0; } else if (maxCol >= this.cols) { maxCol = this.cols - 1; } for (let r = minRow; r <= maxRow; r++) { for (let c = minCol; c <= maxCol; c++) { this._cells[r][c].addItem(item); } } } public removeItem(go: T) { let minRow = Math.floor((go.y - go.height / 2) / this.cellHeight); if (minRow < 0) { minRow = 0; } else if (minRow >= this.rows) { minRow = this.rows - 1; } let maxRow = Math.floor((go.y + go.height / 2) / this.cellHeight); if (maxRow < 0) { maxRow = 0; } else if (maxRow >= this.rows) { maxRow = this.rows - 1; } let minCol = Math.floor((go.x - go.width / 2) / this.cellWidth); if (minCol < 0) { minCol = 0; } else if (minCol >= this.cols) { minCol = this.cols - 1; } let maxCol = Math.floor((go.x + go.width / 2) / this.cellWidth); if (maxCol < 0) { maxCol = 0; } else if (maxCol >= this.cols) { maxCol = this.cols - 1; } for (let r = minRow; r <= maxRow; r++) { for (let c = minCol; c <= maxCol; c++) { this._cells[r][c].removeItem(go); } } } private initTiles() { const a = new Array<Array<GridCell<T>>>(this.rows); for (let r = 0; r < this.rows; r++) { const b = a[r] = new Array<GridCell<T>>(this.cols); for (let c = 0; c < this.cols; c++) { b[c] = new GridCell<T>(); } } return a; } }