Skip to content

Commit

Permalink
feat(ecs): add automatic key assign to component
Browse files Browse the repository at this point in the history
  • Loading branch information
ruggero-visintin committed Dec 8, 2023
1 parent 01f73dc commit 9b6beda
Show file tree
Hide file tree
Showing 12 changed files with 42 additions and 16 deletions.
1 change: 1 addition & 0 deletions src/core/decorators/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './typeDecorator';
9 changes: 9 additions & 0 deletions src/core/decorators/typeDecorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export function Type(value: string) {
return function (constructor: Function) {
constructor.prototype.__type = value;
};
}

export function typeOf(object: any): string {
return object.__type;
}
3 changes: 2 additions & 1 deletion src/core/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './color';
export * from './color';
export * from './decorators';
2 changes: 2 additions & 0 deletions src/ecs/components/BaseComponent.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Type } from "../../core";
import { IEntity } from "../entities";
import { IComponent } from "./IComponent";

@Type('BaseComponent')
export class BaseComponent implements IComponent {
private _container: IEntity | null = null;

Expand Down
3 changes: 2 additions & 1 deletion src/ecs/components/MaterialComponent.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Color } from "../../core";
import { Color, Type } from "../../core";
import { BaseComponent } from "./BaseComponent";

@Type('MaterialComponent')
export class MaterialComponent extends BaseComponent {
public diffuseColor: Color = Color.fromHex('#d16cd8');
public opacity: number = 100;
Expand Down
2 changes: 2 additions & 0 deletions src/ecs/components/ShapeComponent.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Type } from "../../core";
import { Renderer, DrawPrimitiveCommand, PrimitiveType} from "../../renderer";
import { BaseComponent } from "./BaseComponent";
import { MaterialComponent } from "./MaterialComponent";
Expand All @@ -6,6 +7,7 @@ import { TransformComponent } from "./TransformComponent";
/**
* Represents a primitive Shape like rectangle, circle, etc
*/
@Type('ShapeComponent')
export class ShapeComponent extends BaseComponent {
public shapeType: PrimitiveType = PrimitiveType.Rectangle;

Expand Down
2 changes: 2 additions & 0 deletions src/ecs/components/TransformComponent.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Type } from "../../core";
import { BaseComponent } from "./BaseComponent";

interface Position2D {
Expand All @@ -10,6 +11,7 @@ interface Size2D {
height: number;
}

@Type('TransformComponent')
export class TransformComponent extends BaseComponent {
public position: Position2D = { x: 0, y: 0 };
public size: Size2D = { width: 0, height: 0 };
Expand Down
16 changes: 11 additions & 5 deletions src/ecs/entities/BaseEntity.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import { BaseComponent, IComponent } from "../components";
import { typeOf } from "../../core";
import { IComponent } from "../components";
import { IEntity } from "./IEntity";

export class BaseEntity implements IEntity {
private components: Map<string, IComponent> = new Map();

public addComponent(key: string, component: IComponent): void {
this.components.set(key, component);
public addComponent(component: IComponent): void {
this.components.set(typeOf(component), component);
component.setContainer(this);
}

public getComponent<Component extends IComponent>(key: string): Component | undefined {
return this.components.get(key) as Component;
/**
* Gets the first component matching a specific type
* @param type
* @returns the first component found with the type
*/
public getComponent<Component extends IComponent>(type: string): Component | undefined {
return this.components.get(type) as Component;
}
}
4 changes: 2 additions & 2 deletions src/ecs/entities/IEntity.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { IComponent } from "../components";

export interface IEntity {
addComponent(key: string, component: IComponent): void;
getComponent<T extends IComponent>(key: string): T | undefined;
addComponent(component: IComponent): void;
getComponent<T extends IComponent>(type: string): T | undefined;
}
10 changes: 5 additions & 5 deletions test/unit/ecs/entities/BaseEntity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,24 @@ describe('ecs/entities/BaseEntity', () => {
it('Should add component to entity', () => {
const testComponent = new BaseComponent();

baseEntity.addComponent('testComponent', testComponent);
expect(baseEntity.getComponent<BaseComponent>('testComponent')).toEqual(testComponent);
baseEntity.addComponent(testComponent);
expect(baseEntity.getComponent<BaseComponent>('BaseComponent')).toEqual(testComponent);
});

it('Should set the entity as container of the given component', () => {
const testComponent = new BaseComponent();

baseEntity.addComponent('testComponent', testComponent);
baseEntity.addComponent(testComponent);
expect(testComponent.getContainer()).toEqual(baseEntity);
})
})

describe('.getComponent()', () => {
it('Should retrieve a specific component given a specific key', () => {
const testComponent = new BaseComponent();
baseEntity.addComponent('testComponent', testComponent);
baseEntity.addComponent( testComponent);

expect(baseEntity.getComponent<BaseComponent>('testComponent')).toEqual(testComponent);
expect(baseEntity.getComponent<BaseComponent>('BaseComponent')).toEqual(testComponent);
})
})
})
2 changes: 2 additions & 0 deletions test/unit/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
"baseUrl": "../../",
"esModuleInterop": true,
"strict": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
Expand Down
4 changes: 2 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
"experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
"emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
Expand Down

0 comments on commit 9b6beda

Please sign in to comment.