# Code for the Components

Let's create a folder src/components and add 5 files to it:

  • componentFactories.ts
  • PositionComponent.ts
  • SpriteComponent.ts
  • PickupComponent.ts
  • CameraFocusComponent.ts

# src/components/componentFactories.ts

import { createComponentFactoryRegistry } from "typed-ecstasy";

import type { SampleContext, SampleEntityConfig } from "../types";

// We define a component factory registry here, which knows the types we want to use
export const componentFactories = createComponentFactoryRegistry<SampleEntityConfig, SampleContext>();

Ignore the types SampleEntityConfig and SampleContext for now, we'll get to them later.

# src/components/CameraFocusComponent.ts

import { Component } from "typed-ecstasy";

import { componentFactories } from "./componentFactories";

// First of all, we need the component itself. This is what you will interact with in your entity systems.
export class CameraFocusComponent extends Component {
    public weight = 1;

    // Optional: You can implement a reset method, which will be called if pooling is in place.
    // It will be called when the component gets removed and the pool didn't reach its maximum size yet.
    // But: This is rarely necessary, since you will mostly reset the values in the factory below.
    // It might be a valid use-case if the component keeps references that might prevent garbage collection.
    // public reset() {
    //     this.weight = 1;
    // }
}

// Then we need a configuration type, i.e. the data that is needed to assemble your entity correctly
// To be able to configure the above component using a data-driven approach,
// we need to first define, what properties can be configured using that data.
// The following interface represent your json data:
export type CameraFocusConfig = {
    weight?: number;
};

// Finally, we need to register a factory, which reads values from the blueprint and assigns it to the new component.
componentFactories.add(
    // The first parameter must be a key from SampleEntityConfig.
    "CameraFocus",
    // The second parameter is a factory function to assemble the component.
    (obtain, blueprint, context) => {
        // Use obtain() to create a component rather than creating one using `new`. This allows us to use object pooling.
        const comp = obtain(CameraFocusComponent);
        // Use blueprint.get to receive configuration properties
        // It will automatically know the property names and types as specified in the config type above.
        comp.weight = blueprint.get(
            // blueprint.get() has autocompletion for the properties you defined in the config type above!
            "weight",
            // The second parameter is a fallback value, which gets used when the blueprint does not have a value for the specified key.
            // Its type is matched against the type in your config type above.
            // Check out ../types.ts to find out what a context is!
            context.defaultCameraFocusWeight
        );

        // A component factory must return a fully initialized component or null if you want to skip adding this component for some reason.
        return comp;
    }
);

# src/components/PositionComponent.ts

import { Component } from "typed-ecstasy";

import { componentFactories } from "./componentFactories";

export class PositionComponent extends Component {
    public x = 0;
    public y = 0;
}

export type PositionConfig = {
    x?: number;
    y?: number;
};

componentFactories.add("Position", (obtain, blueprint) => {
    const comp = obtain(PositionComponent);
    comp.x = blueprint.get("x", 1);
    comp.y = blueprint.get("y", 2);
    return comp;
});

# src/components/SpriteComponent.ts

import { Component } from "typed-ecstasy";

import { componentFactories } from "./componentFactories";

export class SpriteComponent extends Component {
    public image = "";
    public layer = 0;
}

export type SpriteConfig = {
    image: string;
    layer?: number;
};

componentFactories.add("Sprite", (obtain, blueprint) => {
    const comp = obtain(SpriteComponent);
    comp.image = blueprint.get("image", "notfound.png");
    comp.layer = blueprint.get("layer", 1);
    return comp;
});

# src/components/PickupComponent.ts

import { Component } from "typed-ecstasy";

import { componentFactories } from "./componentFactories";

export class PickupComponent extends Component {
    public material: "stone" | "wood" = "wood";
    public amount = 1;
}

export type PickupConfig = {
    material: "stone" | "wood";
    amount: number;
};

componentFactories.add("Pickup", (obtain, blueprint) => {
    const comp = obtain(PickupComponent);
    comp.material = blueprint.get("material", "wood");
    comp.amount = blueprint.get("amount", 1);
    return comp;
});