I know @Calinou already posted the definitive link, but I'll quote the most relevant code for this discussion. The full tutorial gets into more advanced OOP state machines, which are great, but may be too much if you are just learning or want something quick to play around with.
enum State
{
STATE_STANDING,
STATE_JUMPING,
STATE_DUCKING,
STATE_DIVING
};
void Heroine::handleInput(Input input)
{
switch (state_)
{
case STATE_STANDING:
if (input == PRESS_B)
{
state_ = STATE_JUMPING;
yVelocity_ = JUMP_VELOCITY;
setGraphics(IMAGE_JUMP);
}
else if (input == PRESS_DOWN)
{
state_ = STATE_DUCKING;
setGraphics(IMAGE_DUCK);
}
break;
case STATE_JUMPING:
if (input == PRESS_DOWN)
{
state_ = STATE_DIVING;
setGraphics(IMAGE_DIVE);
}
break;
case STATE_DUCKING:
if (input == RELEASE_DOWN)
{
state_ = STATE_STANDING;
setGraphics(IMAGE_STAND);
}
break;
}
}
Note, that is C++ code, but could be converted to GDScript easily. The key is that you have a defined set of states as an enum, and then your update code simply uses a case/match statement to process a particular state each frame (and that you can only be in any one state at any given time).
When starting out you (without state machines) typically have a bunch of boolean variables like "is_jumping", "is_ducking", "is_attacking", but it gets very confusing if you want to jump and attack at the same time. Or maybe you are ducking and then you attack, and then press jump during the attack animation. It quickly gets unmanageable. FSMs solve this very nicely. You know for sure which state you are in, and you can easily code it so that if you press attack while ducking, you transition to the attack state (where you are standing). And if you are attacking, you don't respond to the jump button being pressed. It is way cleaner.