Adding simplistic Physics
For the step i wanted to add collisions.
The words and letters were already colliding with the wall, so letting them collide with each other seemed like a natural way to progress.
Features
The controls stay the same as for the text spawner, this time though, if two blocks of text collide the change direction and color.
Upcoming
Instead of showing letters we could go for displaying images.
Also state transitioning between a menu and the game itself might be a good idea.
Details
The "physics engine" really is as simplistic as it can get while still hopefully open for later changes.
The velocity handling and collision with the world bounds have moved into the physics engine, with a fixed step size.
The PhysicsEngine does not directly accept Entites from the GameModel.
They first need to be wrapped into a Proxy to be used in any PhysicsEngine.
It keeps the physic body's bounding box, a unique ID and a reference to the proxied Entity.
It is important to remember to add and remove Entities from the physics engine appropriatly, other wise "Ghost"-bodies could produce unwanted effects.
export class CollisionProxy { public static next_id = 0; public id: number = CollisionProxy.next_id++; constructor( public outerBox: Rect, public reference: CollisionProxiable, ) { } /** * A collision has occurred between this entity and another. * @param other * @param collision */ public onCollision(other: CollisionProxy, collision: Collision) { if ( !!this.reference.onCollision ) { this.reference.onCollision(other, collision); } } }
This CollisionProxy is extended for the currently in use, by the velocity.
And that is all that physics directly interacts with.
On the other hand this also means, that the outerBox and velocity objects should always be synched to the entites posistion values used for rendering. In this case i simply kept the refernce to them in the entity and made sure to only read-access them.
The collision handling itself is just brute force AABB checking for now.
The collisions in the step will also be saved to the PhysicsEngine if some logic would be easier to handle outside of an Entity.
public checkCollisions() { this.collisions = []; this.proxies.forEach(proxy => { this.proxies.forEach(other => { // don't collide with yourself if (proxy === other) return; // get overlapping rectangle between the two boxes const overlap = proxy.outerBox.overlap(other.outerBox); // if the overlap is a "real" rectangle, then there is a collision if (overlap.bottom > overlap.top && overlap.right > overlap.left) { this.handleCollision(proxy, other, overlap); } }); }); }
If a collision is detected first the physics will be solved by moving the two boxes apart and adjusting the velocity.
After that it will call the collision callback on the two boxes.
public handleCollision(proxy: AABBCollisionProxy, other: AABBCollisionProxy, overlap: BoundingBox) { // create a collision object const collision : AABBCollision = { overlap: overlap, a: proxy, b: other, } // solve the collision on physics side this.solveCollision(collision); // call the onCollision callback for each box proxy.onCollision(other, collision); other.onCollision(proxy, collision); // add the collision to the list of collisions this.collisions.push(collision); }
Overall there are many things, easily optimizable right now, so i will probably come back to this a lot.
Improvement Ideas:
- Collisions should be bound to the colliding entities and be able to persist over multiple steps (allowing collision "start" and "end" events)
- Opimize the performance for adding and removing proxies to the engine.
- Allow multistep resolving of the physics collision to smothen out the results.
- Provide a config object to selectivly enable or disable certain bevhaviours during collision.
Comments