diff --git a/examples/simpleCollision/index.html b/examples/simpleCollision/index.html
index 68c3317a..76e69494 100644
--- a/examples/simpleCollision/index.html
+++ b/examples/simpleCollision/index.html
@@ -13,48 +13,44 @@
const physx = new SparkEngine.Physx();
const physicsSystem = new SparkEngine.PhysicsSystem(physx);
- const firstGameObject = new SparkEngine.GameObject();
- firstGameObject.transform.size = { width: 20, height: 150 };
- firstGameObject.material.diffuseColor = new SparkEngine.Rgb(255, 0, 0);
- const firstGameObjectBB = new SparkEngine.BoundingBoxComponent();
- firstGameObject.addComponent(firstGameObjectBB);
- firstGameObjectBB.aabb = { ...firstGameObject.transform.position, ...firstGameObject.transform.size }
-
- const secondGameObject = new SparkEngine.GameObject();
- secondGameObject.transform.size = { width: 20, height: 150 };
- secondGameObject.transform.position = { x: 280, y: 0 };
- secondGameObject.material.diffuseColor = new SparkEngine.Rgb(0, 0, 255);
-
- secondGameObjectBB = new SparkEngine.BoundingBoxComponent();
- secondGameObject.addComponent(secondGameObjectBB);
- secondGameObjectBB.aabb = { ...secondGameObject.transform.position, ...secondGameObject.transform.size }
+ const leftWall = new SparkEngine.StaticObject();
+ leftWall.transform.size = { width: 20, height: 150 };
+ leftWall.material.diffuseColor = new SparkEngine.Rgb(255, 0, 0);
+ leftWall.boundingBox.matchContainerTransform = true;
+
+ const rightWall = new SparkEngine.StaticObject();
+ rightWall.transform.size = { width: 20, height: 150 };
+ rightWall.transform.position = { x: 280, y: 0 };
+ rightWall.material.diffuseColor = new SparkEngine.Rgb(0, 0, 255);
+ rightWall.boundingBox.matchContainerTransform = true;
const pingPongBall = new SparkEngine.GameObject();
pingPongBall.transform.size = { width: 20, height: 20 };
pingPongBall.transform.position = { x: 140, y: 70 };
pingPongBall.material.diffuseColor = new SparkEngine.Rgb(0, 255, 0);
- const pingPongBoundingBox = new SparkEngine.BoundingBoxComponent()
- pingPongBall.addComponent(pingPongBoundingBox);
- pingPongBoundingBox.aabb = { ...pingPongBall.transform.position, ...pingPongBall.transform.size }
- renderSystem.registerComponent(firstGameObject.shape);
- renderSystem.registerComponent(secondGameObject.shape);
+ const pingPongBB = new SparkEngine.BoundingBoxComponent()
+ pingPongBB.matchContainerTransform = true;
+
+ pingPongBall.addComponent(pingPongBB);
+
+ renderSystem.registerComponent(leftWall.shape);
+ renderSystem.registerComponent(rightWall.shape);
renderSystem.registerComponent(pingPongBall.shape);
- physicsSystem.registerComponent(firstGameObjectBB);
- physicsSystem.registerComponent(secondGameObjectBB);
- physicsSystem.registerComponent(pingPongBoundingBox);
+ physicsSystem.registerComponent(leftWall.boundingBox);
+ physicsSystem.registerComponent(rightWall.boundingBox);
+ physicsSystem.registerComponent(pingPongBB);
let velocity = 5;
- pingPongBoundingBox.onCollisionCb = () => {
+ pingPongBB.onCollisionCb = () => {
velocity *= -1;
pingPongBall.transform.position.x += velocity;
}
const drawFrame = () => {
pingPongBall.transform.position.x += velocity;
- pingPongBoundingBox.aabb.x = pingPongBall.transform.position.x;
physicsSystem.update();
physx.simulate();
diff --git a/src/ecs/components/BoundingBoxComponent.ts b/src/ecs/components/BoundingBoxComponent.ts
index d02796b7..baea0251 100644
--- a/src/ecs/components/BoundingBoxComponent.ts
+++ b/src/ecs/components/BoundingBoxComponent.ts
@@ -1,6 +1,7 @@
import { Type } from "../../core";
import { PhysicsObject, Physx } from "../../physx";
import { BaseComponent } from "./BaseComponent";
+import { TransformComponent } from "./TransformComponent";
import { CollisionCallback, ICollidableComponent } from "./interfaces";
interface AABB {
@@ -12,11 +13,19 @@ interface AABB {
@Type('BoundingBoxComponent')
export class BoundingBoxComponent extends BaseComponent implements ICollidableComponent {
- public onCollisionCb: CollisionCallback | undefined;
private defaultAABB: AABB = { x: 0, y: 0, width: 0, height: 0 };
+ public onCollisionCb: CollisionCallback | undefined;
+ public matchContainerTransform: boolean = false;
+
public get aabb(): AABB {
- return this.defaultAABB;
+
+ const container = this.getContainer();
+ const containerTransform = container ? container.getComponent('TransformComponent') : undefined;
+
+ return this.matchContainerTransform && containerTransform
+ ? { ...containerTransform.position, ...containerTransform.size }
+ : this.defaultAABB
}
public set aabb(value: AABB) {
diff --git a/src/ecs/entities/StaticObject.ts b/src/ecs/entities/StaticObject.ts
new file mode 100644
index 00000000..fa01dc86
--- /dev/null
+++ b/src/ecs/entities/StaticObject.ts
@@ -0,0 +1,12 @@
+import { BoundingBoxComponent } from "../components";
+import { GameObject } from "./GameObject";
+
+export class StaticObject extends GameObject {
+ public boundingBox = new BoundingBoxComponent();
+
+ constructor() {
+ super();
+
+ this.addComponent(this.boundingBox);
+ }
+}
\ No newline at end of file
diff --git a/src/ecs/entities/index.ts b/src/ecs/entities/index.ts
index 7084a62f..42c66002 100644
--- a/src/ecs/entities/index.ts
+++ b/src/ecs/entities/index.ts
@@ -1,3 +1,4 @@
export * from './BaseEntity';
export * from './IEntity';
-export * from './GameObject';
\ No newline at end of file
+export * from './GameObject';
+export * from './StaticObject';
\ No newline at end of file
diff --git a/test/unit/ecs/components/BoundingBoxComponent.test.ts b/test/unit/ecs/components/BoundingBoxComponent.test.ts
index a662d3d8..7a41b9bc 100644
--- a/test/unit/ecs/components/BoundingBoxComponent.test.ts
+++ b/test/unit/ecs/components/BoundingBoxComponent.test.ts
@@ -1,4 +1,4 @@
-import { BoundingBoxComponent, ICollidableComponent, Physx } from "../../../../src";
+import { BaseEntity, BoundingBoxComponent, GameObject, ICollidableComponent, Physx } from "../../../../src";
describe('ecs/components/BoundingBoxComponent', () => {
let bbComponent = new BoundingBoxComponent();
@@ -9,6 +9,69 @@ describe('ecs/components/BoundingBoxComponent', () => {
physx = new Physx();
})
+ describe('get aabb()', () => {
+ it.each([
+ {
+ aabb: { x: 0, y: 0, width: 0, height: 0 },
+ expected: { x: 0, y: 0, width: 0, height: 0 }
+ },
+ {
+ aabb: { x: 5, y: 10, width: 15, height: 5 },
+ expected: { x: 5, y: 10, width: 15, height: 5 }
+ }
+ ])('Should return the default aabb by default', (test) => {
+ bbComponent.aabb = test.aabb;
+ expect(bbComponent.aabb).toEqual(test.expected);
+ })
+
+ it('Should return the container entity AABB if .matchContainerTransform is true', () => {
+ const parentEntity = new GameObject();
+ parentEntity.addComponent(bbComponent);
+
+ bbComponent.matchContainerTransform = true;
+
+ parentEntity.transform.position = { x: 15, y: 25 };
+ parentEntity.transform.size = { width: 10, height: 10 };
+
+ expect(bbComponent.aabb).toEqual({
+ x: 15, y: 25, width: 10, height: 10
+ })
+ });
+
+ it('Should return the default AABB if .matchContainerTransform is true but parent container is null', () => {
+ bbComponent.matchContainerTransform = true;
+ bbComponent.aabb.x = 555;
+
+ expect(bbComponent.aabb).toEqual({
+ x: 555, y: 0, width: 0, height: 0
+ })
+ });
+
+ it('Should return the default AABB if .matchContainerTransform is false', () => {
+ const parentEntity = new GameObject();
+ parentEntity.addComponent(bbComponent);
+
+ bbComponent.matchContainerTransform = false;
+ bbComponent.aabb.x = 555;
+
+ expect(bbComponent.aabb).toEqual({
+ x: 555, y: 0, width: 0, height: 0
+ })
+ })
+
+ it('Should return the default AABB if .matchContainerTransform is true but parent container transform is null', () => {
+ const parentEntity = new BaseEntity();
+ parentEntity.addComponent(bbComponent);
+
+ bbComponent.matchContainerTransform = true;
+ bbComponent.aabb.x = 555;
+
+ expect(bbComponent.aabb).toEqual({
+ x: 555, y: 0, width: 0, height: 0
+ })
+ })
+ })
+
describe('.update()', () => {
it.each([{
@@ -24,15 +87,15 @@ describe('ecs/components/BoundingBoxComponent', () => {
expect(physx.physicalWorld).toEqual(expect.arrayContaining([{
object: {
aabb: test.expected
- },
+ },
onCollisionCallback: expect.any(Function)
}]));
});
it('Should trigger the onCollision callback when a collision is registered', () => {
- const cbBBComponentA = jest.fn(() => {});
- const cbBBComponentB = jest.fn(() => {});
+ const cbBBComponentA = jest.fn(() => { });
+ const cbBBComponentB = jest.fn(() => { });
bbComponent.aabb.x = 5;
bbComponent.aabb.y = 5;
@@ -53,5 +116,5 @@ describe('ecs/components/BoundingBoxComponent', () => {
expect(cbBBComponentA).toHaveBeenCalled();
expect(cbBBComponentB).toHaveBeenCalled();
})
- })
+ });
})
\ No newline at end of file
diff --git a/test/unit/ecs/entities/StaticObject.test.ts b/test/unit/ecs/entities/StaticObject.test.ts
new file mode 100644
index 00000000..3dce1a2b
--- /dev/null
+++ b/test/unit/ecs/entities/StaticObject.test.ts
@@ -0,0 +1,25 @@
+import { BoundingBoxComponent, GameObject, StaticObject } from "../../../../src"
+
+describe('ecs/entities/StaticObject', () => {
+ let staticObject = new StaticObject();
+
+ afterEach(() => {
+ staticObject = new StaticObject();
+ })
+
+ describe('.constructor()', () => {
+ it('Should extend GameObject class', () => {
+ expect(staticObject).toBeInstanceOf(GameObject);
+ })
+
+ it('Should have a bounding box by default', () => {
+ expect(staticObject.boundingBox).toEqual(expect.objectContaining({
+ aabb: { x: 0, y: 0, width: 0, height: 0 },
+ }));
+ })
+
+ it('Should register this entity as a container of its default components', () => {
+ expect(staticObject.boundingBox.getContainer()).toBe(staticObject);
+ })
+ })
+})
\ No newline at end of file