diff --git a/arcade/physics_engines.py b/arcade/physics_engines.py index 78e417291..ed4f0599a 100644 --- a/arcade/physics_engines.py +++ b/arcade/physics_engines.py @@ -298,6 +298,7 @@ def __init__(self, gravity_constant: float = 0.5, ladders: Optional[Union[SpriteList, Iterable[SpriteList]]] = None, walls: Optional[Union[SpriteList, Iterable[SpriteList]]] = None, + jump_delay: int = 0 ): self._ladders: Optional[List[SpriteList]] self._platforms: List[SpriteList] @@ -322,6 +323,8 @@ def __init__(self, self.gravity_constant: float = gravity_constant self.jumps_since_ground: int = 0 self.allowed_jumps: int = 1 + self.jump_delay = jump_delay + self.jump_ticks = jump_delay self.allow_multi_jump: bool = False # The property object for ladders. This allows us setter/getter/deleter capabilities in safe manner @@ -378,17 +381,17 @@ def is_on_ladder(self): # Check for touching a ladder if self.ladders: hit_list = check_for_collision_with_lists(self.player_sprite, self.ladders) - if len(hit_list) > 0: + if hit_list and self.jump_ticks >= self.jump_delay: + self.jumps_since_ground = 0 return True return False - def can_jump(self, y_distance: float = 5) -> bool: + def is_on_ground(self, y_distance: float = 5) -> bool: """ Method that looks to see if there is a floor under - the player_sprite. If there is a floor, the player can jump - and we return a True. + the player_sprite. If there is a floor, we return a True. - :returns: True if there is a platform below us + :returns: True if there is a platform below us. """ # Move down to see if we are on a platform @@ -399,10 +402,24 @@ def can_jump(self, y_distance: float = 5) -> bool: self.player_sprite.center_y += y_distance - if len(hit_list) > 0: + if hit_list: self.jumps_since_ground = 0 + return True + else: + return False - if len(hit_list) > 0 or self.allow_multi_jump and self.jumps_since_ground < self.allowed_jumps: + def can_jump(self, y_distance: float = 5) -> bool: + """ + Method that looks to see if there is a floor under + the player_sprite. If there is a floor, the player can jump + and we return a True. + + :returns: True if there is a platform below us + """ + + if (self.is_on_ground(y_distance) or self.is_on_ladder() or self.allow_multi_jump and + self.jumps_since_ground < self.allowed_jumps and + self.jump_ticks >= self.jump_delay): return True else: return False @@ -414,9 +431,10 @@ def enable_multi_jump(self, allowed_jumps: int): (1 allows only a single jump, 2 enables double-jump, etc) If you enable multi-jump, you MUST call increment_jump_counter() - every time the player jumps. Otherwise they can jump infinitely. + every time the player jumps. Otherwise, they can jump infinitely. - :param allowed_jumps: + :param allowed_jumps: Maximum number of jumps allowed + :param jump_delay: Number of ticks before another jump is allowed """ self.allowed_jumps = allowed_jumps self.allow_multi_jump = True @@ -432,10 +450,43 @@ def disable_multi_jump(self): self.allowed_jumps = 1 self.jumps_since_ground = 0 - def jump(self, velocity: int): - """ Have the character jump. """ - self.player_sprite.change_y = velocity - self.increment_jump_counter() + def jump(self, velocity: int, + air_jump_velocity: Optional[int] = None, + air_jump_style: Optional[str] = "set", + jump_velocity_limit: Optional[int] = None): + """ Have the character jump. Multijump can be set with a separate in-air velocity and air jumps can be + set to be additive, limited, or a set value. Additive only adds to the player's change_y velocity. Limited + will add to the players' change_y until the jump_velocity limit. Set always sets the players velocity + to their air jump speed. """ + + # Sets air_jump_velocity to the same as ground jumps if no velocity is specified + if not air_jump_velocity: + air_jump_velocity = velocity + + if self.can_jump(): + # Air Jump logic + if self.jumps_since_ground > 0: + if air_jump_style == "additive": + self.player_sprite.change_y += air_jump_velocity + elif air_jump_style == "limited": + if not jump_velocity_limit: + jump_velocity_limit = air_jump_velocity + if self.player_sprite.change_y < 0: + self.player_sprite.change_y = air_jump_velocity + elif self.player_sprite.change_y + air_jump_velocity < jump_velocity_limit: + self.player_sprite.change_y += air_jump_velocity + else: + self.player_sprite.change_y = jump_velocity_limit + elif air_jump_style == "set": + self.player_sprite.change_y = air_jump_velocity + else: + raise ValueError("Air jump style set is not valid. Use additive, limited, or set.") + + # Ground Jump Logic + else: + self.player_sprite.change_y = velocity + + self.increment_jump_counter() def increment_jump_counter(self): """ @@ -443,6 +494,7 @@ def increment_jump_counter(self): """ if self.allow_multi_jump: self.jumps_since_ground += 1 + self.jump_ticks = 0 def update(self): """ @@ -460,6 +512,8 @@ def update(self): # print(f"Spot F ({self.player_sprite.center_x}, {self.player_sprite.center_y})") # print(f"Spot B ({self.player_sprite.center_x}, {self.player_sprite.center_y})") + if self.jump_ticks < self.jump_delay: + self.jump_ticks += 1 for platform_list in self.platforms: for platform in platform_list: