From e3744241ea030568d1245c4c24079579306d1712 Mon Sep 17 00:00:00 2001 From: Yan Wittmann Date: Sat, 25 Mar 2023 17:18:43 +0100 Subject: [PATCH] First implementation of physics --- project/main.py | 45 ++++++++++++++++++----- project/physics/PhysicsElementsHandler.py | 35 ++++++++++-------- project/sprite/BoundingBox.py | 3 ++ project/sprite/DynamicSprite.py | 2 +- project/sprite/Sprite.py | 44 ++++++++++++++-------- project/sprite/StaticSprite.py | 24 ++++++++---- 6 files changed, 104 insertions(+), 49 deletions(-) diff --git a/project/main.py b/project/main.py index 724c8ba..b05acd9 100644 --- a/project/main.py +++ b/project/main.py @@ -12,7 +12,18 @@ from sprite.Sprite import Sprite from sprite.StaticSprite import StaticSprite from ui_elements.TextLabel import TextLabel -what_to_run = 'textlabel' +what_to_run = 'physics' + + +def apply_frame_rate(number: float): + """ + this function calculates a factor that will be multiplied with the + physics of the game to provide a constant speed + :param number: The number to scale by the factor + :return: The scaled number + """ + return number / (frame_rate / 30) + if what_to_run == 'level': csv_parse_test = LevelManager('data/levels') @@ -27,30 +38,44 @@ elif what_to_run == 'physics': screen = pygame.display.set_mode((600, 500)) pygame.display.set_caption("PE GAME") clock = pygame.time.Clock() + frame_rate = 50 spritesheet_manager = SpritesheetManager("data/sprites", "data/sprites/sprites.json") - test_1_sprite = DynamicSprite(spritesheet_manager.get_sheet("test_1")) - test_2_sprite = StaticSprite(spritesheet_manager.get_sheet("test_1")) - - test_1_sprite.position_scale = PositionScale((10, 10), (1, 1)) - test_2_sprite.position_scale = PositionScale((100, 100), (1, 1)) - physics_handler = PhysicsElementsHandler() + + test_1_sprite = DynamicSprite(spritesheet_manager.get_sheet("test_1")) + test_1_sprite.position_scale = PositionScale((10, -100), (1, 1)) physics_handler.add_sprite(test_1_sprite) + + test_3_sprite = DynamicSprite(spritesheet_manager.get_sheet("test_1")) + test_3_sprite.position_scale = PositionScale((130, 100), (1, 1)) + test_3_sprite.motion = (-4, -11) + physics_handler.add_sprite(test_3_sprite) + + test_2_sprite = StaticSprite(spritesheet_manager.get_sheet("test_1")) + test_2_sprite.position_scale = PositionScale((10, 80), (1, 1)) physics_handler.add_sprite(test_2_sprite) - while True: - clock.tick(10) + while True: + clock.tick(frame_rate) + + skip = False for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() exit(0) + elif event.type == pygame.KEYDOWN: + if event.key == pygame.K_RIGHT: + skip = False + + if skip: + continue screen.fill((0, 0, 0)) - physics_handler.tick(1) + physics_handler.tick(apply_frame_rate(1)) physics_handler.draw(screen, screen_transform) pygame.display.update() diff --git a/project/physics/PhysicsElementsHandler.py b/project/physics/PhysicsElementsHandler.py index 11e8392..1a63cdb 100644 --- a/project/physics/PhysicsElementsHandler.py +++ b/project/physics/PhysicsElementsHandler.py @@ -6,6 +6,8 @@ from sprite.Sprite import Sprite from sprite.StaticSprite import StaticSprite MOTION_STEPS = 10 +TOLERANCE = 1 + class PhysicsElementsHandler: def __init__(self): @@ -38,24 +40,27 @@ class PhysicsElementsHandler: sorted_dynamic_sprites = sorted(dynamic_sprites, key=lambda spr: spr.position_scale.position[1]) for sprite in sorted_dynamic_sprites: - total_motion = sprite.motion - motion_step = (total_motion[0] / MOTION_STEPS, total_motion[1] / MOTION_STEPS) + self.attempt_move(sprite, colliders, MOTION_STEPS) - for i in range(MOTION_STEPS): - sprite.position_scale.position = ( - sprite.position_scale.position[0] + motion_step[0], - sprite.position_scale.position[1] + motion_step[1] - ) + def attempt_move(self, sprite: DynamicSprite, colliders: list[StaticSprite], motion_steps: int) -> bool: + total_motion = sprite.motion + motion_step = (total_motion[0] / motion_steps, total_motion[1] / motion_steps) - for collider in colliders: - if sprite is not collider and sprite.collides_with(collider): - sprite.position_scale.position = ( - sprite.position_scale.position[0] - motion_step[0], - sprite.position_scale.position[1] - motion_step[1] - ) + for i in range(motion_steps): + sprite.position_scale.position = ( + sprite.position_scale.position[0] + motion_step[0], + sprite.position_scale.position[1] + motion_step[1] + ) - sprite.motion = (0, 0) - break + for collider in colliders: + if sprite is not collider and sprite.collides_with(collider, TOLERANCE): + sprite.position_scale.position = ( + sprite.position_scale.position[0] - motion_step[0], + sprite.position_scale.position[1] - motion_step[1] + ) + return False + + return True def draw(self, screen: Surface, screen_transform: PositionScale): for sprite in self.sprites: diff --git a/project/sprite/BoundingBox.py b/project/sprite/BoundingBox.py index d6eb886..135c676 100644 --- a/project/sprite/BoundingBox.py +++ b/project/sprite/BoundingBox.py @@ -11,3 +11,6 @@ class BoundingBox: def get_position(self): return self.x, self.y + + def __str__(self): + return f"({self.x}, {self.y}, {self.width}, {self.height})" diff --git a/project/sprite/DynamicSprite.py b/project/sprite/DynamicSprite.py index 6dd9207..2f93a11 100644 --- a/project/sprite/DynamicSprite.py +++ b/project/sprite/DynamicSprite.py @@ -14,7 +14,7 @@ class DynamicSprite(StaticSprite): self.motion = (0, 0) self.deceleration_horizontal = 0 - self.gravity = 0 + self.gravity = 9.81 / 10 def tick(self, dt: float): super().tick(dt) diff --git a/project/sprite/Sprite.py b/project/sprite/Sprite.py index 0628199..02ad616 100644 --- a/project/sprite/Sprite.py +++ b/project/sprite/Sprite.py @@ -52,31 +52,43 @@ class Sprite: return if self.image is not None: - target_position = screen_transform.apply_scale_to_position() - target_position = ( - target_position[0] + self.position_scale.position[0], - target_position[1] + self.position_scale.position[1] - ) + # target_position = screen_transform.apply_scale_to_position() + # target_position = ( + # target_position[0] + self.position_scale.position[0], + # target_position[1] + self.position_scale.position[1] + # ) - target_scale = ( - screen_transform.scale[0] * self.position_scale.scale[0], - screen_transform.scale[1] * self.position_scale.scale[1] - ) - target_size = ( - int(target_scale[0] * self.image.get_width()), - int(target_scale[1] * self.image.get_height()) - ) + # target_scale = ( + # screen_transform.scale[0] * self.position_scale.scale[0], + # screen_transform.scale[1] * self.position_scale.scale[1] + # ) + # target_size = ( + # int(target_scale[0] * self.image.get_width()), + # int(target_scale[1] * self.image.get_height()) + # ) + + # target_image = self.get_scaled_image(self.image, target_size) + + # screen.blit(target_image, target_position) + + target_scale = (screen_transform.scale[0] * self.position_scale.scale[0], + screen_transform.scale[1] * self.position_scale.scale[1]) + + target_position = ((self.position_scale.position[0] + screen_transform.position[0]) * target_scale[0], + (self.position_scale.position[1] + screen_transform.position[1]) * target_scale[1]) + + target_size = (int(target_scale[0] * self.image.get_width()), + int(target_scale[1] * self.image.get_height())) target_image = self.get_scaled_image(self.image, target_size) - screen.blit(target_image, target_position) def get_bounding_box(self) -> BoundingBox: return BoundingBox( self.position_scale.position[0], self.position_scale.position[1], - self.image.get_width(), - self.image.get_height() + self.image.get_width() * self.position_scale.scale[0], + self.image.get_height() * self.position_scale.scale[1] ) def get_scaled_image(self, image, resize): diff --git a/project/sprite/StaticSprite.py b/project/sprite/StaticSprite.py index a346c08..5efa776 100644 --- a/project/sprite/StaticSprite.py +++ b/project/sprite/StaticSprite.py @@ -11,9 +11,7 @@ class StaticSprite(Sprite): def __init__(self, spritesheet: Spritesheet): super().__init__(spritesheet) - self.position = (0, 0) - - def collides_with(self, collider: 'StaticSprite'): + def collides_with(self, collider: 'StaticSprite', tolerance: float = 0.0): if not self.is_collider or not collider.is_collider: return False @@ -26,16 +24,28 @@ class StaticSprite(Sprite): self_position = self_bounds.get_position() other_position = other_bounds.get_position() - if self_position[0] + self_dimensions[0] < other_position[0]: + # if self_position[0] + self_dimensions[0] < other_position[0]: + # return False + + # if self_position[0] > other_position[0] + other_dimensions[0]: + # return False + + # if self_position[1] + self_dimensions[1] < other_position[1]: + # return False + + # if self_position[1] > other_position[1] + other_dimensions[1]: + # return False + + if self_position[0] + self_dimensions[0] < other_position[0] + tolerance: return False - if self_position[0] > other_position[0] + other_dimensions[0]: + if self_position[0] > other_position[0] + other_dimensions[0] - tolerance: return False - if self_position[1] + self_dimensions[1] < other_position[1]: + if self_position[1] + self_dimensions[1] < other_position[1] + tolerance: return False - if self_position[1] > other_position[1] + other_dimensions[1]: + if self_position[1] > other_position[1] + other_dimensions[1] - tolerance: return False return True