2
\$\begingroup\$

I'm doing a game using Phaser with TypeScript. I want to implement a component based architecture for Actors. What I'm trying to achieve is this:

var component:AwesomeComponent = actor.getComponent<AwesomeComponent>();

I know there's a workaround by constructing objects of type T. But I don't like it because I'd probably need to define tons of different constructors to represent all differents kinds of components.

What I'm doing so far is passing a string with the type (of course a number would be better, but I'm still prototyping):

getComponent<T>(componentType:string): T 
{
    for (let i = 0; i < this.components.length; i++) 
    {
        if (componentType == this.components[i].componentType)
        {
            return <T><any>this.components[i];
        }
    }
    return undefined;
}

It works, but the result is not good, you have to type the class name twice:

var animComp = actor.getComponent<AnimationComponent>("AnimationComponent");

Any suggestions on how to solve it?

Thank you for your time!

\$\endgroup\$
  • \$\begingroup\$ This seems to me like more of a typescript specific issue, you might have more luck over at the main stack overflow site. \$\endgroup\$ – Nate Aug 28 '17 at 21:56
  • \$\begingroup\$ I already solved this, one guy helped me in the Phaser forum. Thanks, Nate! \$\endgroup\$ – Guilherme Recchi Cardozo Aug 28 '17 at 23:13
  • \$\begingroup\$ Maybe post the solution here and accept it, so others finding it here can benefit too. \$\endgroup\$ – Nate Aug 30 '17 at 14:31
2
\$\begingroup\$

I've got some good suggestions on Phaser forum, the solution shown by user @flyovergames was what I was looking for:

<script src="https://raw.githubusercontent.com/photonstorm/phaser/master/v2/src/Phaser.js"></script>
class Component {
    public actor: Actor;
}

interface ComponentType<T extends Component> { new(...args: any[]): T }

class Actor {
    public components: Component[] = [];

    private getComponentIndex<T extends Component>(type: ComponentType<T>): number {
        for (let i = 0; i < this.components.length; ++i) {
            const component: Component = this.components[i];
            if (component instanceof type) {
                return i;
            }
        }
        return -1;
    }

    public hasComponent<T extends Component>(type: ComponentType<T>): boolean {
        return this.getComponentIndex(type) !== -1;
    }

    public getComponent<T extends Component>(type: ComponentType<T>, ...args: any[]): T {
        const index: number = this.getComponentIndex(type);
        if (index !== -1) {
            return this.components[index] as T;
        }
        return this.addComponent(type, ...args);
    }

    public addComponent<T extends Component>(type: ComponentType<T>, ...args: any[]): T {
        if (this.hasComponent(type)) { return this.getComponent(type); }
        const component: T = new type(...args); // this needs ES5
        component.actor = this;
        this.components.push(component);
        return component;
    }

    public delComponent<T extends Component>(type: ComponentType<T>): T {
        const component: Component = this.getComponent(type);
        this.components.splice(this.components.indexOf(component), 1);
        component.actor = undefined;
        return component as T;
    }
}

class AwesomeComponent extends Component {
    public message: string;
    constructor(message: string) {
        super();
        this.message = message;
    }
    public alert(): void {
        console.log(this.message);
    }
}

function test(): void {
    const actor: Actor = new Actor();
    if (actor.hasComponent(AwesomeComponent)) { throw new Error(); }
    actor.addComponent(AwesomeComponent, "awesome!");
    const awesome: AwesomeComponent = actor.getComponent(AwesomeComponent); // automatic cast!
    awesome.alert();
    actor.delComponent(AwesomeComponent);
    if (actor.hasComponent(AwesomeComponent)) { throw new Error(); }
}

test();

Link to full thread: Solution

\$\endgroup\$
  • 1
    \$\begingroup\$ Instead of just dumping a link here that might die, you should quote the most relevant part of the link here as per this help page. \$\endgroup\$ – Tyyppi_77 Sep 1 '17 at 12:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.