Critical hits are common in many games. Today I’m going to discuss the thought process and math behind calculating critical hits in the upcoming shooter Super Monday Night Combat.
Following Precedent
What are critical hits? How do they work? Typically there is some percent chance to crit which causes an attack to deal some percentage extra damage. Commons values are in the ranges 1-30% chance to crit and 35-300% extra damage. In an RPG this works as you’d expect. For each swing of the sword you roll the magic random number generator dice and check for a crit1.
Team Fortress 2 has an interesting crit system2. Guns have a 2% base chance to crit which temporarily increases as the player deals damage. Slow firing guns (shotgun) check for crit with each shot similar to an RPG sword. Rapid fire guns (uzi) check once per second and crit in two second bursts.
Remember Your Units
The TF2 rapid fire crit system is interesting. Crits are about dealing burst damage. A minigun firing 10% bullets with crit evenly distributed is less burst and more DPS gain. A two-second crit window is perfect for dealing burst damage. However it has a slight issue. With bursts what does a crit chance 10% really mean. How many bullets are fired critically? Is it 50% or some other number? Let’s think it through!
Assume a two second crit time, massive 50% crit chance, and one second crit check interval. On average the player will fail the first dice roll when they start firing and pass the 2nd roll after one second. The dice is not rolled while critical firing. After two seconds of critical firing we roll the dice3. One second later, on average, we’ll critical fire again. With a 50% crit chance the pattern is 1-second regular, 2-seconds crit, 1-second, regular, 2-seconds crit. That 50% magically became 66%!
The number of critical bullets is not exactly equal to the listed crit chance and varies with the crit duration. Even weapon fire rate potentially matters depending on when your roll the dice. A slow-ish weapon that fires every .9 seconds could fire either two or three times within a two second crit window. That doesn’t mean that TF2 is wrong. It just means the true effect of crit chance is both variable and obfuscated.
Something Simpler
For Super Monday Night Combat we established a few goals. First and foremost the crit percent should represent the number of bullets that deal critical damage no matter the weapon. Second, crits should not be extended so no rolling the dice mid-crit. Third, crits can occur directly after one another. There is no “dead time”. Finally, we want one system to rule them all! Easier to understand, easier to maintain.
In our previous example a crit chance of 50% had an effective crit rate 66%. This means there is a mystery number that generates an effective crit rate of 50%. We can find that number with math!
1 2 3 4 5 6 7 8 9 10 11 12 | CritChance = balance_number; // [0,1] percentage FireRate = balance_number; // Bullets per sec CritDuration = balance_number; // In seconds critChancePerBullet = CritChance; bulletsPerMinute = (FireRate * 60); critBulletsPerMinute = (critChancePerBullet * bulletsPerMinute); bulletsPerCrit = (1 + int(bulletsPerSecond * CritDuration)); critsPerMinute = (critBulletsPerMinute / bulletsPerCrit); nonCritBulletsPerMinute = (bulletsPerMinute - (critsPerMinute*bulletsPerCrit)); critChecksPerMinute = nonCritBulletsPerMinutes; realCritChancePerCheck = (critsPerMinute / critChecksPerMinute); |
A wall of math, hooray! Let’s go over the interesting line numbers.
1-3: Values defined by a designer that vary per weapon.
6: Math is done in “per minute” space here as it’s easier to visualize 2 checks per minute rather than .0333 checks per second. Term cancellation is left as an exercise to the reader.
8: The 1 term is because at least one bullet will fire crit even with a CritDuration of 0. The int cast is to truncate as fractional bullets don’t exist.
10: This is where the magic happens! We want to check for crit for every bullet except those that are already critical. We know have many bullets we want to be critical so we can calculate the number of non-critical bullets which is also the number of times we’ll check for crit. Line 11 is unnecessary but present for blog post clarity.
12: The pieces are in place and the real crit chance for each roll of the dice can be easily calculated4.
Huzzah! This will result in a realCritChancePerCheck that will correctly cause a CritChance percent of bullets to be fired critically no matter the crit chance, fire rate, or crit duration. Exactly as desired.
Conclusion
This is a specific example of work I did this past week. It fixed some bugs, simplified a system, and produced a result that is easy to comprehend which leads to better balance faster. I had fun sitting down with a piece of paper to solve the equation and felt it was interesting enough to share. It’s not a magical solution for every game, but it works for ours.
As an open question to the readers, how do you calculate critical hits in your game? What methods have you found useful?