planck-js
Version:
2D JavaScript/TypeScript physics engine for cross-platform HTML5 game development
103 lines (87 loc) • 2.71 kB
text/typescript
/*
* Planck.js
*
* Copyright (c) Ali Shakiba
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
export interface DataDriverListener<D, R> {
enter: (d: D) => R | null;
exit: (d: D, ref: R) => void;
update: (d: D, ref: R) => void;
}
/**
* @experimental
*
* DataDriver is used it to create, update and destroy physics entities based on game objects.
*/
export class DataDriver<D extends object, R> {
/** @internal */ private _refMap: Record<string, R> = {};
/** @internal */ private _listener: DataDriverListener<D, R>;
/** @internal */ private _key: (d: D) => string;
constructor(key: (d: D) => string, listener: DataDriverListener<D, R>) {
this._key = key;
this._listener = listener;
}
/** @internal */ private _map: Record<string, D> = {};
// just for reuse
/** @internal */ private _xmap: Record<string, D> = {};
/** @internal */ private _data: D[] = [];
/** @internal */ private _entered: D[] = [];
/** @internal */ private _exited: D[] = [];
update(data: (D | null)[]) {
// todo: use diff-match-patch instead of map?
if (!Array.isArray(data)) throw "Invalid data: " + data;
this._entered.length = 0;
this._exited.length = 0;
this._data.length = data.length;
for (let i = 0; i < data.length; i++) {
if (typeof data[i] !== "object" || data[i] === null) continue;
const d = data[i];
const id = this._key(d);
if (!this._map[id]) {
this._entered.push(d);
} else {
delete this._map[id];
}
this._data[i] = d;
this._xmap[id] = d;
}
for (const id in this._map) {
this._exited.push(this._map[id]);
delete this._map[id];
}
const temp = this._map;
this._map = this._xmap;
this._xmap = temp;
for (let i = 0; i < this._exited.length; i++) {
const d = this._exited[i];
const key = this._key(d);
const ref = this._refMap[key];
this._listener.exit(d, ref);
delete this._refMap[key];
}
for (let i = 0; i < this._entered.length; i++) {
const d = this._entered[i];
const key = this._key(d);
const ref = this._listener.enter(d);
if (ref) {
this._refMap[key] = ref;
}
}
for (let i = 0; i < this._data.length; i++) {
if (typeof data[i] !== "object" || data[i] === null) continue;
const d = this._data[i];
const key = this._key(d);
const ref = this._refMap[key];
this._listener.update(d, ref);
}
this._entered.length = 0;
this._exited.length = 0;
this._data.length = 0;
}
ref(d: D): R {
return this._refMap[this._key(d)];
}
}