This system is fiendish, because you’ll find out soon, that it needs to do much more than you think at first.
The basic (but eventually too simple) double jump part or basic ground check is easy to do. In the jump state where you apply force (or set velocity), simple add another button down, and you can jump mid-air. To get back to start, add a system global event ON COLLISION. But that’s not enough, and maybe not the best way to do this.
What if your character runs off a platform. Then you’re not grounded, either. What if, you bump with the head into a platform above? The collision would think you’re grounded, even when you’re not. What is on slopes or bumping into a wall?
This is what makes it fiendish. You likely need a more robust system. You have a few options. The basic principle is that a separate FSM checks all the time, sets the animator and isGrounded bool. Other FSM read that when necessary.
(1) You can make a child game object with a collider set as trigger that sticks out underneath (only) of your character. It could be a circle that is slightly smaller than the base circle collider, and slightly offset downwards, resulting in crescent at the base that can handle most cases. You can use on trigger, enter and stay to be quite precise whether there is ground under the feet.
(2) You can also constantly cast a overlap sphere (see method below), to check for ground underneath.
(3) Generally recommended by many is the use of several rays. Like, three (downwards, and diagonal to detect slopes). Switch on the debug line to see where the rays are cast and fiddle around how far, angles etc. You can dial that in before anything else. Make sure you use the correct ray version (raycast 2d), and use store did hit to flip a bool. If you use several rays, you need to collate them. For that, I use Bool Any True, that is, if any of the ground detection bools is true, then say that the character is grounded.
// stumbled upon old post, clarified the ray variant.