Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Arcade [PyPi Release History](https://pypi.org/project/arcade/#history) page.
## Unreleased

### Fixes
- Fixed a player in `PhysicsEnginePlatformer` sliding along when standing across two moving platforms. It moved by both platforms' speeds added together, so it slid until it stood on only one. It now moves with the platform it stands on the most, and isn't moved by a platform it only touches the edge of while standing mostly on something still ([#2782](https://github.com/pythonarcade/arcade/issues/2782)).
- Fixed the PyInstaller hook bundling Arcade's version file at `arcade/VERSION/VERSION`. Frozen apps printed an error on startup and reported `arcade.VERSION` as `0.0.0`, and builds using `collect_all("arcade")` failed because the hook and `collect_all` put a folder and a file at the same path. The version file is now `arcade/_VERSION`, so it can't be confused with the `arcade.version` module ([#2837](https://github.com/pythonarcade/arcade/issues/2837)).
- Fixed drawing into a texture atlas losing new drawing when the atlas grows during `render_into()`. Nested rendering and exception exits now restore the previous camera and render region.
- Fixed several `arcade.create_text_sprite()` bugs:
Expand Down
17 changes: 12 additions & 5 deletions arcade/physics_engines.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,14 +324,21 @@ def _move_sprite(
# print(f"Spot X ({self.player_sprite.center_x}, {self.player_sprite.center_y})"
# f" {self.player_sprite.change_y}")
elif moving_sprite.change_y < 0:
# Reset number of jumps
# Move straight up out of what we landed on, which also works
# for ramps
for item in hit_list_x:
# Move straight up, which also works for ramps
_move_out_of_sprite(moving_sprite, item, 1.0, 0.25)

# NOTE: Not all sprites have velocity
if getattr(item, "change_x", 0.0) != 0:
moving_sprite.center_x += item.change_x # type: ignore
# Move with what the sprite stands on the most, or if that's a
# tie, the highest. Moving with each moving platform it touches
# would add their speeds together.
def footing(item: SpriteType) -> tuple[float, float]:
overlap = min(moving_sprite.right, item.right) - max(moving_sprite.left, item.left)
return overlap, item.top

# NOTE: Not all sprites have velocity
standing_on = max(hit_list_x, key=footing)
moving_sprite.center_x += getattr(standing_on, "change_x", 0.0)

# print(f"Spot Y ({self.player_sprite.center_x}, {self.player_sprite.center_y})")
else:
Expand Down
72 changes: 72 additions & 0 deletions tests/unit/physics_engine/test_physics_engine_moving_platforms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"""
A player standing on moving platforms in PhysicsEnginePlatformer moves with
them. Standing across two platforms used to move it by both their speeds
added together, so it slid along until it stood on only one (#2782).
"""

import pytest

import arcade

PLATFORM_WIDTH = 64
SIZE = 32


def _platforms(*speeds):
"""A row of touching platforms with their tops at y = 32."""
platforms = arcade.SpriteList()
for i, speed in enumerate(speeds):
platform = arcade.SpriteSolidColor(
PLATFORM_WIDTH, 32, center_x=100 + i * PLATFORM_WIDTH, center_y=16
)
platform.change_x = speed
platforms.append(platform)
return platforms


def _run(player_x, *speeds, frames=30):
platforms = _platforms(*speeds)
player = arcade.SpriteSolidColor(SIZE, SIZE, center_x=player_x, center_y=48)
engine = arcade.PhysicsEnginePlatformer(player, platforms=platforms, gravity_constant=1)
for _ in range(frames):
engine.update()
return player, platforms


@pytest.mark.parametrize("speed", [2, -3])
def test_one_platform(speed):
player, platforms = _run(100, speed)
assert player.center_x == pytest.approx(100 + 30 * speed)
assert player.bottom == pytest.approx(platforms[0].top)


@pytest.mark.parametrize("speed", [2, -3])
@pytest.mark.parametrize("count", [2, 4])
def test_across_platforms_moving_together(speed, count):
# Centered on the seam between the first two platforms
player, platforms = _run(132, *[speed] * count)
assert player.center_x == pytest.approx(132 + 30 * speed)
assert player.bottom == pytest.approx(platforms[0].top)


@pytest.mark.parametrize(
("player_x", "expected_change"),
[
# Mostly on the left platform, which moves right
(120, 2),
# Mostly on the right platform, which moves left
(144, -2),
],
)
def test_across_platforms_moving_apart(player_x, expected_change):
player, _ = _run(player_x, 2, -2, frames=1)
assert player.center_x == pytest.approx(player_x + expected_change)


def test_moving_platform_next_to_still_one():
# Mostly on a still platform, a little on a moving one
player, _ = _run(120, 0, 2, frames=1)
assert player.center_x == pytest.approx(120)
# Mostly on the moving one
player, _ = _run(144, 0, 2, frames=1)
assert player.center_x == pytest.approx(146)
Loading