excaliburjs / Excalibur

🎮 Your friendly TypeScript 2D game engine for the web 🗡️
https://excaliburjs.com
BSD 2-Clause "Simplified" License
1.82k stars 188 forks source link

New Pool Collection Type to support Object Pooling #136

Closed eonarheim closed 3 years ago

eonarheim commented 10 years ago

Context

Object pooling would bring significant improvements to the memory usage of Excalibur, as well as performance gains related to the construction/destruction of objects.

Proposal

// Creates a new actor pool var pool = new ex.Pool(100);

// must implement IPoolable interface IPoolable{ public reset(): void; public dispose(): void; }

var myActor = pool.getInstance();

// reset and dispose will be called on actor and returned to the pool pool.returnInstance(myActor);

kamranayub commented 10 years ago

Maybe use kill and revive? That way actors could be IPoolable if needed.

eonarheim commented 8 years ago

This will give us a boon on Vector types

kamranayub commented 7 years ago

Best to wait until #722 is merged to do this. Example of possible poolable interfaces are in my comments in PR, copied here:


Here is a full working example using props, mapped types, and pools:

interface VectorProps {
    x: number;
    y: number;
}

function extend(...o) {
    return o.reduce((p, c, i) => {
        return { ...p, ...c };
    });
}

function mixin<T, T2>(instance: T, methods: T2): T & T2 {
    for (var k in methods) {
        (<any>instance)[k] = methods[k];
    }
    return <T & T2>instance;
}

abstract class Class<TProps> {

    abstract defaults: TProps;

    set(props: Partial<TProps>) {
        mixin(this, props);
    }

    reset() {
        this.set(this.defaults);
    }
}

class Vector extends Class<VectorProps> implements VectorProps {
    defaults = { x: 0, y: 0 };

    x: number;
    y: number;

    constructor(props?: Partial<VectorProps>) {
        super();
        this.set(extend(this.defaults, props));
    }
}

interface Poolable<T extends TProps, TProps> {
    create<T extends TProps>(props?: Partial<TProps>): T & PooledInstance;
}

interface PooledInstance {
    poolInstanceMethod();
}

class Pool<T extends Class<TProps>, TProps> {

    constructor(private _ctor: { new (props?: Partial<TProps>): T }) {

    }

    static create<T extends Class<TProps>, TProps>(ctor: { new (props?: Partial<TProps>): T }): Pool<T, TProps>  {        
        var pooled = new Pool<T, TProps>(ctor);

        // do pool stuff

        return pooled;
    }

    create(props?: Partial<TProps>): T & PooledInstance {
        var ins = new this._ctor(props);

        // do pooling stuff

        return mixin(ins, Pool._Mixins);
    };

    // statically define pool mixin functions
    private static _Mixins: PooledInstance = {
        poolInstanceMethod: function () {
            console.log(this, 'pool instance method');
        }
    } 
}

var v = new Vector();
var vp = Pool.create<Vector, VectorProps>(Vector);
var v2 = vp.create({ x: 1, y: 1 });
console.log(v, v.x, v.y);
console.log(vp, v2, v2.x, v2.y);

No real pool instance methods are in the example, but it shows we could add new pool instance methods if need be.

Benefits:

You can play with it by using the Playground.

eonarheim commented 4 years ago

There was an implementation of this put into #1425