Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RuggeroVisintin/issue20 #21

Merged
merged 6 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@ jobs:

- name: install
run: npm ci

- name: test
run: npm run test


- name: build
run: npm run build
test_unit:
Expand Down
2 changes: 1 addition & 1 deletion examples/simpleRect/index.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<html>

<body>
<canvas id="canvas"></canvas>
<canvas id="canvas" style="width: 100%; height: 100%"></canvas>
<script type="text/javascript" src="../../dist/spark-engine-web.js"></script>
<script>
const context = document.getElementById('canvas').getContext('2d')
Expand Down
34 changes: 34 additions & 0 deletions examples/simpleShapeComponent/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<html>

<body>
<canvas id="canvas" style="width: 100%; height: 100%"></canvas>
<script type="text/javascript" src="../../dist/spark-engine-web.js"></script>
<script>
const context = document.getElementById('canvas').getContext('2d')
const device = new SparkEngine.CanvasDevice();

const renderer = new SparkEngine.Renderer(device);
const renderSystem = new SparkEngine.RenderSystem(renderer);

const firstRect = new SparkEngine.ShapeComponent();
firstRect.transform.size = { width: 50, height: 25 };
renderSystem.registerComponent(firstRect);

let pos = 0;

const drawFrame = () => {
pos++;
pos = pos % 60;

firstRect.transform.position = { x: pos, y: pos };


renderSystem.update();
renderer.endFrame(context);
}

setInterval(drawFrame, 33);
</script>
</body>

</html>
4 changes: 4 additions & 0 deletions src/ecs/components/BaseComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,8 @@ export class BaseComponent implements IComponent {
public getContainer(): IEntity {
throw new Error("Method not implemented.");
}

public setContainer(): IEntity {
throw new Error("Method not implemented.");
}
}
1 change: 1 addition & 0 deletions src/ecs/components/IComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ import { IEntity } from "../entities";

export interface IComponent {
getContainer(): IEntity;
setContainer(container: IEntity): void;
}
21 changes: 20 additions & 1 deletion src/ecs/components/ShapeComponent.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
import { PrimitiveType } from "../../renderer/RenderCommand";
import { Renderer } from "../../renderer";
import { DrawPrimitiveCommand, PrimitiveType } from "../../renderer/RenderCommand";
import { BaseComponent } from "./BaseComponent";
import { TransformComponent } from "./TransformComponent";

/**
* Represents a primitive Shape like rectangle, circle, etc
*/
export class ShapeComponent extends BaseComponent {
public shapeType: PrimitiveType = PrimitiveType.Rectangle;

private defaultTransform = new TransformComponent();

public get transform(): TransformComponent {
return this.defaultTransform;
}

public draw(renderer: Renderer): void {
const position = this.transform.position;
const size = this.transform.size;

renderer.pushRenderCommand(new DrawPrimitiveCommand(
PrimitiveType.Rectangle,
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pass this.shapeType instead

[position.x, position.y],
[size.width, size.height]
));
}
}
8 changes: 4 additions & 4 deletions src/ecs/entities/BaseEntity.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { BaseComponent } from "../components";
import { BaseComponent, IComponent } from "../components";
import { IEntity } from "./IEntity";

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

public addComponent<Component extends BaseComponent>(key: string, component: Component): void {
public addComponent(key: string, component: IComponent): void {
this.components.set(key, component);
}

public getComponent<Component extends BaseComponent>(key: string): Component | undefined {
public getComponent<Component extends IComponent>(key: string): Component | undefined {
return this.components.get(key) as Component;
}
}
2 changes: 1 addition & 1 deletion src/ecs/entities/IEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ import { IComponent } from "../components";

export interface IEntity {
addComponent(key: string, component: IComponent): void;
getComponent(key: string): IComponent | undefined;
getComponent<T extends IComponent>(key: string): T | undefined;
}
9 changes: 7 additions & 2 deletions src/ecs/systems/RenderSystem.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import { Renderer } from "../../renderer";
import { ShapeComponent } from "../components";
import { ISystem } from "./ISystem";

export class RenderSystem implements ISystem {
public readonly components: ShapeComponent[] = []
public readonly components: ShapeComponent[] = [];

constructor(
private readonly renderer: Renderer
) {}

public registerComponent(component: ShapeComponent): void {
this.components.push(component);
}

public update(): void {
throw new Error('NotImplemented');
this.components.forEach(component => component.draw(this.renderer));
}
}
11 changes: 7 additions & 4 deletions test/unit/ecs/components/BaseComponent.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { BaseComponent } from "../../../../src";

describe('ecs/components/BaseComponent', () => {
it('Should construct', () => {
new BaseComponent();
describe('.getContainer()', () => {
it.todo('Should get the container entity of the component');
it.todo('Should return null if the component is not attached to an entity');
})

describe('.setConatiner', () => {
it.todo('Should set the container entity to the component');
})
})
18 changes: 18 additions & 0 deletions test/unit/ecs/components/ShapeComponent.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { CanvasDevice, DrawPrimitiveCommand, PrimitiveType, Renderer, ShapeComponent } from "../../../../src";

describe('ecs/components/ShapeComponent', () => {
describe('.draw()', () => {
const shapeComponent = new ShapeComponent();
const renderer = new Renderer(new CanvasDevice());

it('Should push the right draw command to the renderer', () => {
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add more test cases with different positions and sizes

shapeComponent.draw(renderer);

expect(renderer.commandBuffer).toEqual([new DrawPrimitiveCommand(
PrimitiveType.Rectangle,
[0, 0],
[0, 0],
)]);
})
})
})
4 changes: 2 additions & 2 deletions test/unit/ecs/entities/BaseEntity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ describe('ecs/entities/BaseEntity', () => {
it('Should add component to entity', () => {
const testComponent = new BaseComponent();

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

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

expect(baseEntity.getComponent<BaseComponent>('testComponent')).toEqual(testComponent);
})
Expand Down
23 changes: 21 additions & 2 deletions test/unit/ecs/systems/RenderSystem.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { RenderSystem, ShapeComponent } from "../../../../src"
import { CanvasDevice, RenderSystem, Renderer, ShapeComponent } from "../../../../src";

describe('systems/RenderSystem', () => {
describe('.registerComponent()', () => {
const renderSystem = new RenderSystem();
const renderSystem = new RenderSystem(new Renderer(new CanvasDevice()));
const myTestShape = new ShapeComponent();

it('Should register the component into the RenderSystem components list', () => {
Expand All @@ -11,4 +11,23 @@ describe('systems/RenderSystem', () => {
expect(renderSystem.components).toContain(myTestShape);
})
})

describe('.update()', () => {
const renderSystem = new RenderSystem(new Renderer(new CanvasDevice()));

it('Should draw renderable object', () => {
const drawSpy = jest.spyOn(ShapeComponent.prototype, 'draw');

renderSystem.registerComponent(new ShapeComponent());
renderSystem.registerComponent(new ShapeComponent());

renderSystem.update();

expect(drawSpy).toHaveBeenCalledTimes(2);
});

it.todo('Should render objects based on their depthIndex in reverse order (0 rendered last)')

it.todo('Should render object based on their transparency (transparent last)')
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add another test case like it('Should render transparency objects as last but ordered by depth index)

})
})
5 changes: 3 additions & 2 deletions test/unit/renderer/Renderer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@ describe('renderer/Renderer', () => {
[0, 0]
);

// TODO: implement execute interface in renderCommand with gfx as Dependency
// To abstract the logic of how A command is executed from the renderer
const spy = jest.spyOn(renderCommand, 'execute');

renderer.pushRenderCommand(renderCommand);
renderer.endFrame(ctx);

expect(spy).toHaveBeenCalled();
})

it('Should clear the command buffer', () => {
Expand Down