A core feature of Broforce is chaotic chain reactions, and this week our untitled wizarding game gains a bit of boom juice too!
And, bonus: those of you using high refresh rate monitors get a much-needed bugfix.
Explosions
In many games, explosions are a little circle that briefly appears & does some damage to things.
Not here! We do things the hard way!
- Explosions need to damage the terrain: blow holes in walls & leave black soot marks.
- Explosions should set atoms, enemies & players on fire, for beautiful chain reactions.
- Explosions should throw atoms around! Think flinging burning oil everywhere.
- Explosions should push moving bodies and characters away.
- And of course explosions should damage characters (and make them gib if they die).
Touching so many systems means explosions are a house of cards that require only the slightest tap to start behaving oddly. Here's a small taste of some problems I ran into:
- Explosions should set things on fire but fire spreads slowly & that doesn't match the "big boom" aesthetic.
- So now explosions bypass the usual mechanism for fire & set atoms alight directly. 1
- Initially I had explosions spawn fire but fire atoms last too long & end up looking like firework
- So now explosions spawn a new dedicated "spark" atom.
- If you just draw a circle to find atoms that should be pushed away, then the explosion can appear to push atoms that are behind walls.
- So I draw lines (raycast) from the center of the circle to the circumference, and the explosive power gets reduced when air is encountered.
- Explosions should push moving bodies away, so you need to know which parts of moving bodies are hit so they spin somewhat-accurately - which is somewhat computationally expensive.
- So since I'm raycasting anyway, the raycasts set the velocity of the atoms attached to moving bodies, and bodies now accumulate the velocity of their atoms. 2
I'm pretty sure I haven't got it quite right yet but at least they look okay and that's good enough for now.
Screenshake
Explosions were looking okay but still feeling bad, so I added screenshake:
The internet has strong opinions on how to do screenshake right, but Juicing Your Camera With Math's approach was easy to implement:
- Keep track of a
trauma
level from 0.0 - 1.0, which fades over time. - Rotate and translate the game's camera3 by the square of the trauma (so lower trauma -> much less displacement).
- Modify that rotation/translation by some Perlin noise so that it's smooth.
If screenshake makes you sick, you can turn it down, off - or up! - in the View menu.
Refresh Rates and Game Loops
As a starving artist, I use a modest4 60hz screen, and so the game is supposed to update its simulation about 60 times a second.
But those of you on 120hz screens (hello Apple fans! Also hello FPS players!) have been experiencing the game running twice as quickly as intended!
The details are only interesting to programmers,5 so I'll just say here that it was much more effort6 to fix than it should have been, but it should be fixed-ish now. 7
Playable web build
Press Shift+B to create a boom at the mouse cursor, and use mouse-wheel (or Shift + number keys) to change its size:
- You might want to press F4 (or press the button) to enter Edit mode so you can fly around freely.
- Also, if you draw moving bodies with B, you can now right click to erase parts of moving bodies.
- Remember that big moving bodies are very heavy and need enormous booms to be pushed! Your character is about 1.6 metres8 tall, just for reference.
Sorry, what was that? You want to create explosions in game as part of actual gameplay? Like, using a spell or something? Hmm..
That's definitely not going to bite me later.
This actually marks a massive change in how moving bodies work: previously moving body atoms would be removed after simulating the atom world, so they didn't exist for e.g. enemies to perceive. But now their atoms stay in the world so things like explosions can impact them (& enemies can see them). This has almost certainly hidden some fantastic new bugs for future Caspar to fix.
Rotating the camera sounds easy but actually it was annoying! The camera was very carefully only drawing enough atoms to fill the screen - so if you rotate the camera, you get whole areas at the edge of the screen that aren't having any atoms rendered. (For now I've "fixed" it by adding a big fudge factor: just wastefully render about 30% more atoms than needed normally.)
38-inch ultra-wide
How to make your game run at 60 FPS is a pretty good overview. I implemented the "fuzzy timing" approach with spiral of death protection - no vsync support.
Macroquad, my current game framework automatically clears inputs received every frame. The key change I made was to run the game simulation 0-N times instead of always running it once (per render): 0-1 times if screen is more than 60hz, OR 2+ times if screen is less than 60hz (or if the simulation ran slowly last time & needs to "make up time"). Combine that with clearing input, and on a e.g. 120hz your keypresses get lost about 50% of the time - very not good! So I had to rewrite a large portion of input handling to work around this limitation.
You may notice a tiny bit of stuttering/jerkiness every so often. Partly this is due to the lack of high performance timers in Macroquad, but also I haven't done anything to detect vsync being on to handle that properly. And ideally I'd also interpolate positions of entities: if you're on a 120hz screen and an entity is moving from X to X+2, you'd want to see the entity at X, X+1 then X+2, rather than X, X, X+2. I'll need to revisit this whole business again when I port my game out of Macroquad (or attempt multiplayer), so I didn't want to put in too much effort now.
Or for our American friends: just over five and a half size 10 shoes (male sizing).