castle-game icon indicating copy to clipboard operation
castle-game copied to clipboard

Rewrite

Open tversteeg opened this issue 2 years ago • 1 comments
trafficstars

Another attempt at rewriting it, this time using the pixels crate for rendering pixels as both a WASM- and a desktop application.

I ended up not liking bevy for my previous attempt. I'm also removing the ECS (for now) since it added a lot of complexity. Furthermore, I'm trying to keep the code as simple as possible without any "smart" solutions for future long-term problems that might not even occur. And nothing screams "keeping-it-simple" like writing your own physics engine, right? I fell in the trap of writing complicated things again…

The physics engine is based on Extended Position Based Dynamics, I'm roughly following this paper but of course only for 2D. The bevy_xpbd crate is also a great source. For collision detection, I first planned to use the parry crate, but I also wanted to give this a shot myself. I got the Separating Axis Theorem to work properly for the narrow-phase collision detection, but I couldn't get proper contact points, so I switched to parry. At first I implemented the broad-phase detection using a Spatial Hash Grid with a fixed bucket size. The performance was not so great and due to all buckets being statically allocated the size of the grid was also very limited. So I switched to the bvh-arena crate.

After getting the physics engine somewhat functional, I did manage that the performance was quite bad. I played around with different benchmarks and profiling to see what was the culprit, and it's mostly related to the data layout of RigidBody, which grew to be a huge struct with 224 bytes of data. Of course iterating over this is not very efficient cache-wise, so I made the decision to locally refactor the rigidbody structure into an ECS, abstracting this behind getters and setters using the handles I previously also used so it wouldn't leak into the rest of the game logic.

2023-08-15_21-50

To make the terrain destructible similarly to Worms and also to allow creation of simple breakable projectiles such as stones or shrapnel, I've implemented a solid shape type, which is a bitmap that automatically generates a sprite with an outline. It also automatically generates a polygon collider by doing a Marching Squares algorithm on the first outline pixel it finds. This is then simplified with an implementation of the Ramer-Douglas-Peucker algorithm.

2023-08-25_22-03

Another major improvement is the assets_manager crate I've integrated. This allows for hot-reloading of assets, which saves a tremendous amount of time with tweaking variables and waiting for re-compiles.

The art style is inspired by the vector art of my previous bevy attempt, but now it's a pixel-art version of that. I'm procedurally generating the terrain in quite a simple way: a buffer is created for each horizontal height and a random factor is to everyone from its previous neighbor, also a direction is changed every set amount of pixels as an additional factor. I could have created a prettier implementation with something like perlin noise but this suffices for now.

http://tversteeg.nl/castle-game/

Some progress screenshares:

collision-detection.webm

rigidbodies.webm

walking-unit.webm

box-debug.webm

draw-hole.webm

tversteeg avatar Jul 21 '23 20:07 tversteeg

Note for finding islands in the solid shape algorithm:

  1. When removing or adding pixels create a bitmap the size of the update rectangle with everything that has been added or removed, two maps if both.
  2. If removed: within the negative pixels of the bitmap with the removal deltas create flood-filled islands for each pixel with an unique ID.
  3. For each edge of an island where there are pixels do a marching squares algorithm to get the collider shape.

tversteeg avatar Aug 25 '23 21:08 tversteeg