1 Step 2b: Jumping, Crouching, and Swimming
sadpmpk edited this page 2023-01-21 02:24:49 -05:00

This section deals with three movement options: jumping, crouching, and swimming.

Jumping

An entity can jump as long as it has at least 1 jump on it. This allows jumping in mid-air (like double jumping). However, when leaving the ground, the entity jump count should be decreased by 1. Conversely, the entity must recover all their jumps if they are landing on ground.

The player should also be allowed to modulate their jump (i.e. short hopping). This can be done by checking if the player has jumped and is moving upwards, then cut their upwards velocity short if the jump action is released. To only allow one short hop for one jump, a short hop flag is maintained. The jump check is necessary because the player could be given upward velocity by other sources, and the short hop should not be allowed for those in general.

To keep track of these states, two components are introduced. One for ground state, and another for jump states:

typedef struct _CMovementState_t
{
    uint8_t ground_state;
}CMovementState_t;

typedef struct _CJump_t
{
    unsigned int jumps;
    unsigned int max_jumps;
    int jump_speed;
    bool jumped;
    bool short_hop;
    unsigned int cooldown_timer;
}CJump_t;

Two bits are used to encode the ground state and its transition. Bit 1 is the current state and bit 2 is the previous state. Therefore, the transitions are encoded as: 10 for leaving the ground and 01 for landing on the ground.

Crouch

Crouching is straightforward enough. Crouching is only possible if the player on the ground. When crouching, the bbox should be reduced in height to just 1 tile height. This allows the player to slip into 1 tile high gap. When releasing crouch, the bbox should be restored back to the original height. The player should crouch as long as the crouch action is active, if possible.

Of course, there are a few edge cases to consider:

  • When leaving the ground (either falling off or jumping), the player must uncrouch, restoring the bbox. It is possible thatthe floor is empty but the ceiling is not empty when uncrouching. Therefore, the collision system should be able to handle this scenario.
  • The player should not be allowed to uncrouch within a 1 tile high gap, but should automatically uncrounch as soon as the ceiling is empty.
  • The player can jump out of crouch. However, this should not be allowed in a 1 tile high gap.

Swimming

Swimming is a bit more complex to deal with. The first step to swimming is to detect whether the player is in water. First, define a water level in a tile.

typedef struct Tile
{
    bool solid;
    unsigned int water_level;
    struct sc_map_64 entities_set;
}Tile_t;

Currently, the water level field will be binary. Those who remember the game will know why the water level is set to unsigned integer.

Then, the water transition is encoded similar to the ground state.

typedef struct _CMovementState_t
{
    uint8_t ground_state;
    uint8_t water_state;
}CMovementState_t;

The water transition check is slightly different for each case. To enter water, the entity's center must be in water. To exit water, however, the entire bbox of the entity should not overlap with water. This behaviour feels nicer when navigating in and out of water.

For physics, the friction is applied in the direction of the entity's velocity. In addition to gravity, upthrust is also applied as long as the center of the entity is in water.

As for the player:

  • The player is allow to 'jump' anytime in water, subjected to a cooldown timer to not allow jump spam.
  • When the player is on ground, it follows the ground controls.
  • The player bbox is the same of crouch when swimming. This allow swimming in 1 tile high gap.

Bounding Box Resizing

Both swimming and crouching will change the bbox of the player. Depending on how the bbox is resized, the player will also need to be move to compensate for the changed bbox. For example, if the player crouches, not only the player's bbox need to decrease, the player need to be shifted downwards to remain on the ground.

To address this, an anchor is set every time the bounding box is to be resize to determine the offset of the entity. 9 anchors are possible: center, top, bottom, left, right, top-left, top-right, bottom-left, and bottom-right. A small snippet of the offset handling is as such:

case AP_TOP_LEFT:
    // When resizing bbox, it is implicitly assumed that to be already in topleft
    // due to the coordindate system (+ve towards right and downwards)
    // So do nothing
return offset;
case AP_TOP_CENTER:
    p1.x = bbox.x / 2;
    p1.y = 0;
    p2.x = new_bbox.x / 2;
    p2.y = 0;
break;
case AP_TOP_RIGHT:
    p1.x = bbox.x;
    p1.y = 0;
    p2.x = new_bbox.x;
    p2.y = 0;
break;

Surprising, the collision system is robust enough to deal with the changing bbox. However, there are edge cases where it breaks down. To avoid this, the bbox of the player is chosen such that these edge cases can be avoided.