Optimize PhysicsElementsHandler.py by checking collisions only with closest sprites.
Improve UiElement.py to cache and reuse scaled images for better performance.main
parent
0778fef354
commit
9dfc487e9a
|
@ -11,7 +11,7 @@
|
||||||
#,#,S,,,,,,,,,,,,,,,,,,,,,#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
|
#,#,S,,,,,,,,,,,,,,,,,,,,,#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
|
||||||
#,#,S,,,,,,,,,,,,,,,,,,,,,#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
|
#,#,S,,,,,,,,,,,,,,,,,,,,,#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
|
||||||
#,#,,,,,,,,,,,,,,,,,,,,,,G,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
|
#,#,,,,,,,,,,,,,,,,,,,,,,G,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
|
||||||
#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
|
#,,,,,,,,,,,,,,,M,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
|
||||||
#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
|
#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
|
||||||
#,,,,,,,,,,,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
|
#,,,,,,,,,,,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
|
||||||
#,L,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
|
#,L,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
|
||||||
|
|
|
Binary file not shown.
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 80 KiB |
150
project/main.py
150
project/main.py
|
@ -23,10 +23,12 @@ WIDTH = 12 * 71 * 1.5
|
||||||
HEIGHT = 12 * 40 * 1.5
|
HEIGHT = 12 * 40 * 1.5
|
||||||
|
|
||||||
# Background to test for level design
|
# Background to test for level design
|
||||||
# test_background_castle = pygame.transform.scale(pygame.image.load('data/sprites/castle_bg.png'), (WIDTH, HEIGHT))
|
test_background_castle = pygame.transform.scale(pygame.image.load('data/sprites/castle_bg.png'), (WIDTH, HEIGHT))
|
||||||
# test_background_cave = pygame.transform.scale(pygame.image.load('data/sprites/cave_bg.png'), (WIDTH, HEIGHT))
|
# test_background_cave = pygame.transform.scale(pygame.image.load('data/sprites/cave_bg.png'), (WIDTH, HEIGHT))
|
||||||
# test_background_tutorial = pygame.transform.scale(pygame.image.load('data/sprites/tutorial_bg.png'), (WIDTH, HEIGHT))
|
# test_background_tutorial = pygame.transform.scale(pygame.image.load('data/sprites/tutorial_bg.png'), (WIDTH, HEIGHT))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def apply_frame_rate(number: float):
|
def apply_frame_rate(number: float):
|
||||||
"""
|
"""
|
||||||
this function calculates a factor that will be multiplied with the
|
this function calculates a factor that will be multiplied with the
|
||||||
|
@ -118,15 +120,15 @@ elif what_to_run == 'level':
|
||||||
screen_transform = PositionScale((0, 0), (1.5, 1.5))
|
screen_transform = PositionScale((0, 0), (1.5, 1.5))
|
||||||
|
|
||||||
pygame.init()
|
pygame.init()
|
||||||
# optimize performance
|
|
||||||
screen = pygame.display.set_mode((12 * ConstantsParser.CONFIG.level_size[0] * screen_transform.scale[0],
|
screen = pygame.display.set_mode((12 * ConstantsParser.CONFIG.level_size[0] * screen_transform.scale[0],
|
||||||
12 * ConstantsParser.CONFIG.level_size[1] * screen_transform.scale[1]),
|
12 * ConstantsParser.CONFIG.level_size[1] * screen_transform.scale[1]),
|
||||||
flags=pygame.HWSURFACE | pygame.DOUBLEBUF,
|
flags=pygame.HWSURFACE | pygame.DOUBLEBUF,
|
||||||
vsync=1)
|
vsync=1,
|
||||||
|
depth=1)
|
||||||
|
|
||||||
pygame.display.set_caption("PM GAME")
|
pygame.display.set_caption("PM GAME")
|
||||||
clock = pygame.time.Clock()
|
clock = pygame.time.Clock()
|
||||||
frame_rate = 30
|
frame_rate = 120
|
||||||
|
|
||||||
spritesheet_manager = SpritesheetManager("data/sprites", "data/sprites/sprites.json")
|
spritesheet_manager = SpritesheetManager("data/sprites", "data/sprites/sprites.json")
|
||||||
sprite_manager = SpriteManager()
|
sprite_manager = SpriteManager()
|
||||||
|
@ -145,6 +147,9 @@ elif what_to_run == 'level':
|
||||||
calculated_frame_rate_text.position_scale.scale = (0.3, 0.3)
|
calculated_frame_rate_text.position_scale.scale = (0.3, 0.3)
|
||||||
sprite_manager.add_ui_element(DrawLayers.UI, calculated_frame_rate_text)
|
sprite_manager.add_ui_element(DrawLayers.UI, calculated_frame_rate_text)
|
||||||
|
|
||||||
|
left_sprite = None
|
||||||
|
right_sprite = None
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
clock.tick(frame_rate)
|
clock.tick(frame_rate)
|
||||||
|
|
||||||
|
@ -159,92 +164,23 @@ elif what_to_run == 'level':
|
||||||
pygame.quit()
|
pygame.quit()
|
||||||
quit()
|
quit()
|
||||||
|
|
||||||
|
for event in click_events:
|
||||||
|
for layer in sprite_manager.layers:
|
||||||
|
for sprite in sprite_manager.layers[layer]:
|
||||||
|
if sprite.get_bounding_box().contains_point(event.world_position):
|
||||||
|
if event.is_click_down(ClickEvent.CLICK_LEFT):
|
||||||
|
left_sprite = sprite
|
||||||
|
if event.is_click_down(ClickEvent.CLICK_RIGHT):
|
||||||
|
right_sprite = sprite
|
||||||
|
|
||||||
|
if left_sprite is not None and right_sprite is not None:
|
||||||
|
print(left_sprite.get_bounding_box().distance(right_sprite.get_bounding_box()))
|
||||||
|
left_sprite = None
|
||||||
|
right_sprite = None
|
||||||
|
|
||||||
screen.fill((0, 0, 0))
|
screen.fill((0, 0, 0))
|
||||||
# Playground to test background on any level
|
# Playground to test background on any level
|
||||||
# screen.blit(test_background_castle, (0, 0))
|
screen.blit(test_background_castle, (0, 0))
|
||||||
|
|
||||||
sprite_manager.tick(TickData(apply_frame_rate(1), pygame_events, key_manager, click_events, screen_transform))
|
|
||||||
sprite_manager.draw(screen, screen_transform)
|
|
||||||
pygame.display.update()
|
|
||||||
|
|
||||||
|
|
||||||
elif what_to_run == 'physics':
|
|
||||||
screen_transform = PositionScale((0, 0), (4, 4))
|
|
||||||
|
|
||||||
pygame.init()
|
|
||||||
screen = pygame.display.set_mode((600, 500))
|
|
||||||
pygame.display.set_caption("PM GAME")
|
|
||||||
clock = pygame.time.Clock()
|
|
||||||
frame_rate = 59.52
|
|
||||||
|
|
||||||
spritesheet_manager = SpritesheetManager("data/sprites", "data/sprites/sprites.json")
|
|
||||||
sprite_manager = SpriteManager()
|
|
||||||
key_manager = KeyManager()
|
|
||||||
|
|
||||||
# test_1_sprite = DynamicSprite(spritesheet_manager.get_sheet("test_1"))
|
|
||||||
# test_1_sprite.position_scale = PositionScale((10, -100), (1, 1))
|
|
||||||
# sprite_manager.add_ui_element(DrawLayers.OBJECTS, 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 = (-9, -10)
|
|
||||||
# sprite_manager.add_ui_element(DrawLayers.OBJECTS, test_3_sprite)
|
|
||||||
|
|
||||||
# test_2_sprite = StaticSprite(spritesheet_manager.get_sheet("test_1"))
|
|
||||||
# test_2_sprite.position_scale = PositionScale((10, 80), (1, 1))
|
|
||||||
# sprite_manager.add_ui_element(DrawLayers.OBJECTS, test_2_sprite)
|
|
||||||
|
|
||||||
for x in range(0, 8):
|
|
||||||
floor_sprite = StaticSprite(spritesheet_manager.get_sheet("test_1"))
|
|
||||||
floor_sprite.position_scale = PositionScale((x * 16, 100), (1, 1))
|
|
||||||
sprite_manager.add_ui_element(DrawLayers.OBJECTS, floor_sprite)
|
|
||||||
for x in range(0, 8):
|
|
||||||
floor_sprite = StaticSprite(spritesheet_manager.get_sheet("test_1"))
|
|
||||||
floor_sprite.position_scale = PositionScale((x * 16, 0), (1, 1))
|
|
||||||
sprite_manager.add_ui_element(DrawLayers.OBJECTS, floor_sprite)
|
|
||||||
|
|
||||||
for x in range(0, 6):
|
|
||||||
floor_sprite = StaticSprite(spritesheet_manager.get_sheet("test_1"))
|
|
||||||
floor_sprite.position_scale = PositionScale((0, x * 16), (1, 1))
|
|
||||||
sprite_manager.add_ui_element(DrawLayers.OBJECTS, floor_sprite)
|
|
||||||
for x in range(0, 6):
|
|
||||||
floor_sprite = StaticSprite(spritesheet_manager.get_sheet("test_1"))
|
|
||||||
floor_sprite.position_scale = PositionScale((130, x * 16), (1, 1))
|
|
||||||
sprite_manager.add_ui_element(DrawLayers.OBJECTS, floor_sprite)
|
|
||||||
|
|
||||||
ghost_character = PlayerSprite(spritesheet_manager.get_sheet("ghost_character"))
|
|
||||||
ghost_character.position_scale = PositionScale((90, 50), (1, 1))
|
|
||||||
sprite_manager.add_ui_element(DrawLayers.OBJECTS, ghost_character)
|
|
||||||
|
|
||||||
text_1 = TextLabel("Frame: 0", 2, 110, 50, alignment="left")
|
|
||||||
text_1.position_scale.scale = (0.1, 0.1)
|
|
||||||
sprite_manager.add_ui_element(DrawLayers.UI, text_1)
|
|
||||||
|
|
||||||
ghost_character.debug_label = text_1
|
|
||||||
|
|
||||||
frame_counter = 0
|
|
||||||
while True:
|
|
||||||
clock.tick(frame_rate)
|
|
||||||
|
|
||||||
skip = False
|
|
||||||
pygame_events: list[pygame.event.Event] = pygame.event.get()
|
|
||||||
key_manager.update_key_events(pygame_events)
|
|
||||||
click_events: list[ClickEvent] = ClickEvent.create_events(pygame_events, screen_transform)
|
|
||||||
|
|
||||||
for event in pygame_events:
|
|
||||||
if event.type == pygame.QUIT:
|
|
||||||
pygame.quit()
|
|
||||||
quit()
|
|
||||||
|
|
||||||
if key_manager.is_keymap_down(KeyManager.KEY_RIGHT):
|
|
||||||
skip = False
|
|
||||||
|
|
||||||
if skip:
|
|
||||||
continue
|
|
||||||
|
|
||||||
frame_counter += 1
|
|
||||||
# text_1.set_text(f"Frame: {frame_counter}")
|
|
||||||
screen.fill((0, 0, 0))
|
|
||||||
|
|
||||||
sprite_manager.tick(TickData(apply_frame_rate(1), pygame_events, key_manager, click_events, screen_transform))
|
sprite_manager.tick(TickData(apply_frame_rate(1), pygame_events, key_manager, click_events, screen_transform))
|
||||||
sprite_manager.draw(screen, screen_transform)
|
sprite_manager.draw(screen, screen_transform)
|
||||||
|
@ -283,41 +219,3 @@ elif what_to_run == 'textlabel':
|
||||||
test3.draw(screen, screen_transform)
|
test3.draw(screen, screen_transform)
|
||||||
|
|
||||||
pygame.display.update()
|
pygame.display.update()
|
||||||
|
|
||||||
|
|
||||||
elif what_to_run == 'sprite':
|
|
||||||
screen_transform = PositionScale((0, 0), (4, 4))
|
|
||||||
|
|
||||||
pygame.init()
|
|
||||||
screen = pygame.display.set_mode((300, 300))
|
|
||||||
pygame.display.set_caption("PE GAME")
|
|
||||||
clock = pygame.time.Clock()
|
|
||||||
|
|
||||||
spritesheet_manager = SpritesheetManager("data/sprites", "data/sprites/sprites.json")
|
|
||||||
|
|
||||||
test_1_sprite = Sprite(spritesheet_manager.get_sheet("test_1"))
|
|
||||||
test_2_sprite = Sprite(spritesheet_manager.get_sheet("test_1"))
|
|
||||||
|
|
||||||
test_1_sprite.position_scale = PositionScale((10, 10), (1, 1))
|
|
||||||
test_2_sprite.position_scale = PositionScale((60, 60), (1, 1))
|
|
||||||
|
|
||||||
# test_1_sprite.dump("debug.png")
|
|
||||||
|
|
||||||
while True:
|
|
||||||
clock.tick(5)
|
|
||||||
|
|
||||||
for event in pygame.event.get():
|
|
||||||
if event.type == pygame.QUIT:
|
|
||||||
pygame.quit()
|
|
||||||
|
|
||||||
screen.fill((0, 0, 0))
|
|
||||||
|
|
||||||
test_1_sprite.tick(1)
|
|
||||||
test_1_sprite.draw(screen, screen_transform)
|
|
||||||
test_2_sprite.tick(1)
|
|
||||||
test_2_sprite.draw(screen, screen_transform)
|
|
||||||
pygame.display.update()
|
|
||||||
|
|
||||||
if random.randint(1, 10) == 1:
|
|
||||||
test_1_sprite.set_animation_state(random.choice(["walk_r", "walk_l", "idle", "other_test"]))
|
|
||||||
print(test_1_sprite.animation_state)
|
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
import math
|
import math
|
||||||
|
import time
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from physics.CollisionDirection import CollisionDirection
|
from physics.CollisionDirection import CollisionDirection
|
||||||
from physics.TickData import TickData
|
from physics.TickData import TickData
|
||||||
|
from sprite.BoundingBox import BoundingBox
|
||||||
from sprite.DynamicSprite import DynamicSprite
|
from sprite.DynamicSprite import DynamicSprite
|
||||||
from sprite.PositionScale import PositionScale
|
from sprite.PositionScale import PositionScale
|
||||||
from sprite.Sprite import Sprite
|
from sprite.Sprite import Sprite
|
||||||
from sprite.StaticSprite import StaticSprite
|
from sprite.StaticSprite import StaticSprite
|
||||||
from ui_elements.UiElement import UiElement
|
from ui_elements.UiElement import UiElement
|
||||||
|
|
||||||
|
MAX_COLLIDER_CHECK_SPRITES = 15
|
||||||
MOTION_STEPS = 10
|
MOTION_STEPS = 10
|
||||||
TOLERANCE = 1
|
TOLERANCE = 1
|
||||||
|
|
||||||
|
@ -27,8 +30,6 @@ class PhysicsElementsHandler:
|
||||||
sprites = []
|
sprites = []
|
||||||
for layer in layers:
|
for layer in layers:
|
||||||
for sprite in layers[layer]:
|
for sprite in layers[layer]:
|
||||||
# if str(type(sprite)) == "<class 'level.elements.dynamic.PushableBoxLevelElement.PushableBoxLevelElement'>":
|
|
||||||
# print(f"Found pushable box")
|
|
||||||
sprite.tick(tick_data)
|
sprite.tick(tick_data)
|
||||||
if isinstance(sprite, Sprite):
|
if isinstance(sprite, Sprite):
|
||||||
sprites.append(sprite)
|
sprites.append(sprite)
|
||||||
|
@ -37,6 +38,8 @@ class PhysicsElementsHandler:
|
||||||
# 1. Find all sprites that have collision enabled and store them in a list
|
# 1. Find all sprites that have collision enabled and store them in a list
|
||||||
# 2. Create a list of all sprites that are dynamic sprites
|
# 2. Create a list of all sprites that are dynamic sprites
|
||||||
# 3. Sort the sprites by their y position
|
# 3. Sort the sprites by their y position
|
||||||
|
# 3. Find the MAX_COLLIDER_CHECK_SPRITES sprites that are closest to the sprite that are colliders but not the
|
||||||
|
# sprite itself
|
||||||
# 4. For each sprite:
|
# 4. For each sprite:
|
||||||
# 4.1. Divide the motion into MOTION_STEPS steps
|
# 4.1. Divide the motion into MOTION_STEPS steps
|
||||||
# 4.2. For each step:
|
# 4.2. For each step:
|
||||||
|
@ -53,8 +56,59 @@ class PhysicsElementsHandler:
|
||||||
dynamic_sprites = [sprite for sprite in sprites if isinstance(sprite, DynamicSprite)]
|
dynamic_sprites = [sprite for sprite in sprites if isinstance(sprite, DynamicSprite)]
|
||||||
sorted_dynamic_sprites = sorted(dynamic_sprites, key=lambda spr: spr.position_scale.position[1])
|
sorted_dynamic_sprites = sorted(dynamic_sprites, key=lambda spr: spr.position_scale.position[1])
|
||||||
|
|
||||||
|
closest_sprites: dict[UiElement, list[tuple[int, StaticSprite]]] = {}
|
||||||
|
|
||||||
|
buffered_bounding_boxes: dict[UiElement, BoundingBox] = {}
|
||||||
|
for collider in colliders:
|
||||||
|
buffered_bounding_boxes[collider] = collider.get_bounding_box()
|
||||||
|
|
||||||
for sprite in sorted_dynamic_sprites:
|
for sprite in sorted_dynamic_sprites:
|
||||||
collides_with = self.attempt_move(tick_data, sprite, colliders, MOTION_STEPS)
|
closest_sprites[sprite] = []
|
||||||
|
current_closest_sprites = closest_sprites[sprite]
|
||||||
|
|
||||||
|
if sprite not in buffered_bounding_boxes:
|
||||||
|
buffered_bounding_boxes[sprite] = sprite.get_bounding_box()
|
||||||
|
|
||||||
|
for collider in colliders:
|
||||||
|
if collider is sprite:
|
||||||
|
continue
|
||||||
|
|
||||||
|
distance = int(buffered_bounding_boxes[collider].distance(buffered_bounding_boxes[sprite]))
|
||||||
|
|
||||||
|
if len(current_closest_sprites) < MAX_COLLIDER_CHECK_SPRITES:
|
||||||
|
current_closest_sprites.append((distance, collider))
|
||||||
|
else:
|
||||||
|
max_index = -1
|
||||||
|
max_distance = -1
|
||||||
|
for i in range(len(current_closest_sprites)):
|
||||||
|
if distance < current_closest_sprites[i][0]:
|
||||||
|
if current_closest_sprites[i][0] > max_distance:
|
||||||
|
max_distance = current_closest_sprites[i][0]
|
||||||
|
max_index = i
|
||||||
|
|
||||||
|
if max_index != -1:
|
||||||
|
current_closest_sprites[max_index] = (distance, collider)
|
||||||
|
|
||||||
|
# set visible false for all those that are not in the closest_sprites list
|
||||||
|
for collider in colliders:
|
||||||
|
found = False
|
||||||
|
for sprite in sorted_dynamic_sprites:
|
||||||
|
for _, c in closest_sprites[sprite]:
|
||||||
|
if c is collider:
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
if found:
|
||||||
|
break
|
||||||
|
collider.visible = not found
|
||||||
|
|
||||||
|
for sprite in sorted_dynamic_sprites:
|
||||||
|
sprite.visible = True
|
||||||
|
|
||||||
|
for sprite in sorted_dynamic_sprites:
|
||||||
|
collides_with = self.attempt_move(tick_data,
|
||||||
|
sprite,
|
||||||
|
[collider for _, collider in closest_sprites[sprite]],
|
||||||
|
MOTION_STEPS)
|
||||||
if collides_with is not None:
|
if collides_with is not None:
|
||||||
for callback in self.collision_callbacks:
|
for callback in self.collision_callbacks:
|
||||||
callback(sprite, collides_with)
|
callback(sprite, collides_with)
|
||||||
|
@ -74,9 +128,7 @@ class PhysicsElementsHandler:
|
||||||
sprite.position_scale.position[1]
|
sprite.position_scale.position[1]
|
||||||
)
|
)
|
||||||
|
|
||||||
# print('Elements: ', list(filter(lambda spr: str(type(spr)) == "<class 'level.elements.LeverInputLevelElement.LeverInputLevelElement'>", colliders)))
|
collides_with = self.check_collides(sprite, colliders)
|
||||||
|
|
||||||
collides_with = self.check_collides(sprite, colliders, tick_data.screen_transform)
|
|
||||||
for collider in collides_with:
|
for collider in collides_with:
|
||||||
if collider is not None:
|
if collider is not None:
|
||||||
if sprite.is_collider and collider.is_collider:
|
if sprite.is_collider and collider.is_collider:
|
||||||
|
@ -120,7 +172,7 @@ class PhysicsElementsHandler:
|
||||||
sprite.position_scale.position[1] + motion_step[1]
|
sprite.position_scale.position[1] + motion_step[1]
|
||||||
)
|
)
|
||||||
|
|
||||||
collides_with = self.check_collides(sprite, colliders, tick_data.screen_transform)
|
collides_with = self.check_collides(sprite, colliders)
|
||||||
for collider in collides_with:
|
for collider in collides_with:
|
||||||
if collider is not None:
|
if collider is not None:
|
||||||
if sprite.is_collider and collider.is_collider:
|
if sprite.is_collider and collider.is_collider:
|
||||||
|
@ -160,7 +212,7 @@ class PhysicsElementsHandler:
|
||||||
|
|
||||||
return collides_with_last
|
return collides_with_last
|
||||||
|
|
||||||
def check_collides(self, sprite: StaticSprite, colliders: list[StaticSprite], screen_transform: PositionScale) -> \
|
def check_collides(self, sprite: StaticSprite, colliders: list[StaticSprite]) -> \
|
||||||
list[StaticSprite]:
|
list[StaticSprite]:
|
||||||
collides_with = []
|
collides_with = []
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
import math
|
||||||
|
|
||||||
|
|
||||||
class BoundingBox:
|
class BoundingBox:
|
||||||
def __init__(self, x, y, width, height):
|
def __init__(self, x, y, width, height):
|
||||||
self.x = x
|
self.x = x
|
||||||
|
@ -5,6 +8,9 @@ class BoundingBox:
|
||||||
self.width = width
|
self.width = width
|
||||||
self.height = height
|
self.height = height
|
||||||
|
|
||||||
|
self.center_x = x + width / 2
|
||||||
|
self.center_y = y + height / 2
|
||||||
|
|
||||||
def get_dimensions(self):
|
def get_dimensions(self):
|
||||||
return self.width, self.height
|
return self.width, self.height
|
||||||
|
|
||||||
|
@ -16,3 +22,27 @@ class BoundingBox:
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"({self.x}, {self.y}, {self.width}, {self.height})"
|
return f"({self.x}, {self.y}, {self.width}, {self.height})"
|
||||||
|
|
||||||
|
def distance(self, bounding_box: 'BoundingBox') -> float:
|
||||||
|
"""
|
||||||
|
Classmates the minimum distance between two bounding boxes by checking in what direction the bounding boxes are
|
||||||
|
in relation to each other.
|
||||||
|
:param bounding_box: The bounding box to compare to.
|
||||||
|
:return: The minimum distance between the two bounding boxes.
|
||||||
|
"""
|
||||||
|
if self.overlaps(bounding_box):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
distance_x = max(0, abs(self.center_x - bounding_box.center_x) - (self.width + bounding_box.width) / 2)
|
||||||
|
distance_y = max(0, abs(self.center_y - bounding_box.center_y) - (self.height + bounding_box.height) / 2)
|
||||||
|
|
||||||
|
return math.sqrt(distance_x ** 2 + distance_y ** 2)
|
||||||
|
|
||||||
|
def overlaps(self, bounding_box: 'BoundingBox') -> bool:
|
||||||
|
"""
|
||||||
|
Checks if the bounding boxes overlap.
|
||||||
|
:param bounding_box: The bounding box to check.
|
||||||
|
:return: True if the bounding boxes overlap, False otherwise.
|
||||||
|
"""
|
||||||
|
return self.x < bounding_box.x + bounding_box.width and self.x + self.width > bounding_box.x and \
|
||||||
|
self.y < bounding_box.y + bounding_box.height and self.y + self.height > bounding_box.y
|
||||||
|
|
|
@ -26,6 +26,7 @@ class Sprite(UiElement):
|
||||||
self.animated = True
|
self.animated = True
|
||||||
|
|
||||||
self.image = None
|
self.image = None
|
||||||
|
self.last_image = None
|
||||||
|
|
||||||
self.is_collider = True
|
self.is_collider = True
|
||||||
self.register_collisions = True
|
self.register_collisions = True
|
||||||
|
@ -60,6 +61,7 @@ class Sprite(UiElement):
|
||||||
self.animation_delay -= animation['delays'][self.animation_frame % len(animation['delays'])]
|
self.animation_delay -= animation['delays'][self.animation_frame % len(animation['delays'])]
|
||||||
self.animation_frame = (self.animation_frame + 1) % len(animation['images'])
|
self.animation_frame = (self.animation_frame + 1) % len(animation['images'])
|
||||||
|
|
||||||
|
self.last_image = self.image
|
||||||
self.image = animation['images'][self.animation_frame % len(animation['images'])]
|
self.image = animation['images'][self.animation_frame % len(animation['images'])]
|
||||||
|
|
||||||
def set_animation_state(self, state: str):
|
def set_animation_state(self, state: str):
|
||||||
|
|
|
@ -21,6 +21,9 @@ class UiElement:
|
||||||
|
|
||||||
self.uuid = uuid.uuid4()
|
self.uuid = uuid.uuid4()
|
||||||
|
|
||||||
|
self.last_image = None
|
||||||
|
self.last_scaled_image = None
|
||||||
|
|
||||||
def add_click_listener(self, listener):
|
def add_click_listener(self, listener):
|
||||||
self.click_listeners.append(listener)
|
self.click_listeners.append(listener)
|
||||||
|
|
||||||
|
@ -49,10 +52,12 @@ class UiElement:
|
||||||
|
|
||||||
image = self.render_sprite_image()
|
image = self.render_sprite_image()
|
||||||
|
|
||||||
if image is not None:
|
target_image = None
|
||||||
target_position = CoordinateTransform.transform_world_to_screen(self.position_scale.position,
|
target_position = CoordinateTransform.transform_world_to_screen(self.position_scale.position,
|
||||||
screen_transform)
|
screen_transform)
|
||||||
|
|
||||||
|
if not self.last_image == image or self.last_scaled_image is None:
|
||||||
|
if image is not None:
|
||||||
screen_scale = screen_transform.scale
|
screen_scale = screen_transform.scale
|
||||||
object_scale = self.position_scale.scale
|
object_scale = self.position_scale.scale
|
||||||
|
|
||||||
|
@ -60,6 +65,12 @@ class UiElement:
|
||||||
int(screen_scale[1] * object_scale[1] * image.get_height()))
|
int(screen_scale[1] * object_scale[1] * image.get_height()))
|
||||||
|
|
||||||
target_image = UiElement.get_scaled_image(image, target_size)
|
target_image = UiElement.get_scaled_image(image, target_size)
|
||||||
|
self.last_scaled_image = target_image
|
||||||
|
self.last_image = image
|
||||||
|
else:
|
||||||
|
target_image = self.last_scaled_image
|
||||||
|
|
||||||
|
if target_image is not None:
|
||||||
screen.blit(target_image, target_position)
|
screen.blit(target_image, target_position)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
Loading…
Reference in New Issue