PIXI Optimized add/remove child container
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as PIXI from 'pixi.js'; | |
// Caution #1: it's written in TypeScript! | |
// Caution #2: it uses the fast element removal method (move last element into removed element's place and change array length) | |
// which changes the order of items in the array, so your rendering order will be affected | |
// Caution #3: events are not triggered | |
export class BufferableContainer extends PIXI.Container | |
{ | |
private readonly childrenToAdd: PIXI.DisplayObject[]; | |
private readonly childrenToRemove: PIXI.DisplayObject[]; | |
private readonly bucketWidth: number; | |
constructor(bucketWidth: number = 24) | |
{ | |
super(); | |
// To optimize the removals we'll group all planned removals into groups based on their horizontal position | |
// So `bucketWidth` equal to 24 means that all children which have x between 0 and 24 (exclusive) will | |
// go to bucket #0, 24-48 to bucket #1 and so on | |
this.bucketWidth = bucketWidth; | |
this.childrenToAdd = []; | |
this.childrenToRemove = []; | |
} | |
public bufferAddChild(child: PIXI.DisplayObject) | |
{ | |
this.childrenToAdd.push(child); | |
} | |
public bufferRemoveChild(child: PIXI.DisplayObject) | |
{ | |
this.childrenToRemove.push(child); | |
} | |
public flushChanges() | |
{ | |
this.flushRemovals(); | |
this.flushAdditions(); | |
this.childrenToAdd.length = 0; | |
this.childrenToRemove.length = 0; | |
this._boundsID++; | |
} | |
private flushRemovals() | |
{ | |
const buckets = this.buildRemovalBucket(); | |
// 1. Iterate through each child | |
// 2. Find the bucket to which the child would belong to | |
// 3. Check against all children in the bucket | |
// 4. If something is found in the bucket, use fast-removal, stop checking this bucket and go to the next child | |
// 5. Once everything is removed, change the children array length | |
// A possible optimization would be to also shrink the bucket after an item from it is used, but I figured it'd be such a negligible | |
// change I didn't implement it in my case. | |
// Also notice that the iteration is inverted - for each child in the container I see if it's supposed to be removed. | |
// This won't work that well if you work with small number of removed elements, but could be easily optimized | |
// to use the old method if there aren't too many things to remove. | |
let newLength = this.children.length; | |
for(let i = 0; i < newLength; i++) { | |
const child = this.children[i]; | |
const bucketId = (child.x / this.bucketWidth) | 0; | |
const bucket = buckets[bucketId] || []; | |
for(let j = 0; j < bucket.length; j++) { | |
const childToRemove = bucket[j]; | |
if (child === childToRemove) { | |
(child as any).parent = null; | |
(child.transform as any)._parentID = -1; | |
this.children[i--] = this.children[--newLength]; | |
break; | |
} | |
} | |
} | |
this.children.length = newLength; | |
} | |
private buildRemovalBucket() | |
{ | |
const buckets: PIXI.DisplayObject[][] = []; | |
for(let i = 0; i < this.childrenToRemove.length; i++) { | |
const child = this.childrenToRemove[i]; | |
// ` <something> | 0` is marginally faster than flooring (microoptimization) but is also nicer to the eye: https://jsperf.com/floor-or-or/6 | |
const bucketId = (child.x / this.bucketWidth) | 0; | |
if (!buckets[bucketId]) { | |
buckets[bucketId] = []; | |
} | |
buckets[bucketId].push(child); | |
} | |
return buckets; | |
} | |
private flushAdditions() | |
{ | |
for(let i = 0; i < this.childrenToAdd.length; i++) { | |
const child: PIXI.DisplayObject = this.childrenToAdd[i]; | |
if (child.parent) { | |
if (child.parent === this) { | |
continue; | |
} | |
child.parent.removeChild(child); | |
} | |
child.parent = this; | |
(child.transform as any)._parentID = -1; | |
this.children.push(child); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment