2D Physics struggles and solutions
Published 2 years ago
A challenge for Captain Miaou
Hi, I am Stephane, developer of the 2D Physics plateformer Miaou Moon. I’d like to share my struggles and solutions when I had to implement the behaviors of Captain Miaou, our game character.
You can check out our game here:
This space cat needed to be able to shrink and grow while precisely check its 2D environment, so he could fit into a crawled space but also get bumped out when he was too big.
Captain Miaou shrinks each time he propels and inflates whenever he eats. To illustrate that, I had 3 set of sprites that I swapped like a skin each time the state of the cat changed: SLIM / NORMAL / FAT, and 1 set of animations to rule them all.
Since these states enabled the cat different proprieties (bigger or lesser damages), it was important that its general aspect reflected that.
To test the environment, its 2D rigibody was provided with a 2D circle collider for the geometry collisions and a bigger 2D circle collider as trigger with the game elements.
When I tried to resize my cat at runtime, the collider wouldn’t change correctly, even if I also tried to resize the 2D collider by code. I believe this bug is corrected now, but not at the time. Another issue was that the function OnTiggerEnter2D wasn’t called when resizing a collider during the same frame a collision occurred.
This resize mechanic IS the heart of our gameplay, so I was like: “NOOOOO, what can I do, we are doomed!!”.
Fortunately there’s this little trick: if I put the cat in a parent GameObject and resized this parent instead, everything worked fine - Don’t ask me why.
First problem solved. Then I was bugged with the fact that the 2D collisions weren’t realistic: using a circle collider meant any limb or helmet part from the cat would go through the decor.
I needed a 2D polygon collider to do this properly. Problem was, my cat’s different states (slim, normal, fat) displayed 3 different shapes, so a simple circle resize wouldn’t work.
I ended up putting 3 different cats under my parent GameObject, where they could be activated or deactivated when needed.
I also had to create 3 separates animation sets and put all sprites on the same Spritesheet, with the different cat parts already set at the correct grow ratio.
Bingo! Either Slim, Normal or Fat, this cat would now bounce happily around all around the place.
My last problem was with the physic itself. In the game, the levels are divided in moons with different physic properties that impact the gameplay: gravity, bounciness, friction. To deal with that,
- I implemented different 2D physics properties to my sprite materials,
- fixed the Mass of the cat at 1
- and changed its Gravity Scale to reflect the different moon gravities.
But when you start to add Physical forces to move your character and implement effects like bumpers and such, you can end up with crazy speeds and rotations, with a sprite too fast for your game camera or ending through the decor.
To limit these speeds I started to put a check on every frame to test my cat velocity and its angularVelocity, to clamp them under reasonable values.
It worked, but the calculation cost was way too much elevated. My final solution was to repeatedly invoke this test and clamp function only every half seconds.
At last, all my 2D physics problems were solved and I was finally able to dig into the level design, which I didn’t know at the time would be the longest part of Miaou Moon development, but this is another story :)
I hope that these technical details can help you save time if you’re also creating a 2D physics game.