Screen to world coordinate translation and click listeners
parent
975948f068
commit
f1709229c5
|
@ -11,9 +11,11 @@ from sprite.SpritesheetManager import SpritesheetManager
|
||||||
from physics.PhysicsElementsHandler import PhysicsElementsHandler
|
from physics.PhysicsElementsHandler import PhysicsElementsHandler
|
||||||
from sprite.Sprite import Sprite
|
from sprite.Sprite import Sprite
|
||||||
from sprite.StaticSprite import StaticSprite
|
from sprite.StaticSprite import StaticSprite
|
||||||
|
from ui_elements.ClickEvent import ClickEvent
|
||||||
from ui_elements.TextLabel import TextLabel
|
from ui_elements.TextLabel import TextLabel
|
||||||
|
from ui_elements.UiElement import UiElement
|
||||||
|
|
||||||
what_to_run = 'level'
|
what_to_run = 'physics'
|
||||||
|
|
||||||
|
|
||||||
def apply_frame_rate(number: float):
|
def apply_frame_rate(number: float):
|
||||||
|
@ -49,20 +51,20 @@ elif what_to_run == 'physics':
|
||||||
|
|
||||||
test_1_sprite = DynamicSprite(spritesheet_manager.get_sheet("test_1"))
|
test_1_sprite = DynamicSprite(spritesheet_manager.get_sheet("test_1"))
|
||||||
test_1_sprite.position_scale = PositionScale((10, -100), (1, 1))
|
test_1_sprite.position_scale = PositionScale((10, -100), (1, 1))
|
||||||
sprite_manager.add_sprite(DrawLayers.OBJECTS, test_1_sprite)
|
sprite_manager.add_ui_element(DrawLayers.OBJECTS, test_1_sprite)
|
||||||
|
|
||||||
test_3_sprite = DynamicSprite(spritesheet_manager.get_sheet("test_1"))
|
test_3_sprite = DynamicSprite(spritesheet_manager.get_sheet("test_1"))
|
||||||
test_3_sprite.position_scale = PositionScale((130, 100), (1, 1))
|
test_3_sprite.position_scale = PositionScale((130, 100), (1, 1))
|
||||||
test_3_sprite.motion = (-9, -10)
|
test_3_sprite.motion = (-9, -10)
|
||||||
sprite_manager.add_sprite(DrawLayers.OBJECTS, test_3_sprite)
|
sprite_manager.add_ui_element(DrawLayers.OBJECTS, test_3_sprite)
|
||||||
|
|
||||||
test_2_sprite = StaticSprite(spritesheet_manager.get_sheet("test_1"))
|
test_2_sprite = StaticSprite(spritesheet_manager.get_sheet("test_1"))
|
||||||
test_2_sprite.position_scale = PositionScale((10, 80), (1, 1))
|
test_2_sprite.position_scale = PositionScale((10, 80), (1, 1))
|
||||||
sprite_manager.add_sprite(DrawLayers.OBJECTS, test_2_sprite)
|
sprite_manager.add_ui_element(DrawLayers.OBJECTS, test_2_sprite)
|
||||||
|
|
||||||
text_1 = TextLabel("Frame: 0", 10, 110, 50, alignment="left")
|
text_1 = TextLabel("Frame: 0", 10, 110, 50, alignment="left")
|
||||||
text_1.position_scale.scale = (0.1, 0.1)
|
text_1.position_scale.scale = (0.1, 0.1)
|
||||||
sprite_manager.add_sprite(DrawLayers.UI, text_1)
|
sprite_manager.add_ui_element(DrawLayers.UI, text_1)
|
||||||
|
|
||||||
counter = 0
|
counter = 0
|
||||||
while True:
|
while True:
|
||||||
|
@ -76,6 +78,24 @@ elif what_to_run == 'physics':
|
||||||
elif event.type == pygame.KEYDOWN:
|
elif event.type == pygame.KEYDOWN:
|
||||||
if event.key == pygame.K_RIGHT:
|
if event.key == pygame.K_RIGHT:
|
||||||
skip = False
|
skip = False
|
||||||
|
elif event.type == pygame.MOUSEBUTTONUP:
|
||||||
|
screen_pos = pygame.mouse.get_pos()
|
||||||
|
world_pos = UiElement.transform_screen_to_world(screen_pos, screen_transform)
|
||||||
|
click_event = ClickEvent(world_pos, screen_pos, event)
|
||||||
|
|
||||||
|
if event.button == 1:
|
||||||
|
test_dyn_sprite = DynamicSprite(spritesheet_manager.get_sheet("test_1"))
|
||||||
|
test_dyn_sprite.position_scale = PositionScale(world_pos, (1, 1))
|
||||||
|
sprite_manager.add_ui_element(DrawLayers.OBJECTS, test_dyn_sprite)
|
||||||
|
elif event.button == 3:
|
||||||
|
test_dyn_sprite = StaticSprite(spritesheet_manager.get_sheet("test_1"))
|
||||||
|
test_dyn_sprite.position_scale = PositionScale(world_pos, (1, 1))
|
||||||
|
sprite_manager.add_ui_element(DrawLayers.OBJECTS, test_dyn_sprite)
|
||||||
|
|
||||||
|
elif event.button == 2:
|
||||||
|
for element in sprite_manager.find_elements_at_position(world_pos):
|
||||||
|
sprite_manager.remove_ui_element(element)
|
||||||
|
element.click(click_event)
|
||||||
|
|
||||||
if skip:
|
if skip:
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from pygame import Surface
|
from pygame import Surface
|
||||||
|
|
||||||
from physics.PhysicsElementsHandler import PhysicsElementsHandler
|
from physics.PhysicsElementsHandler import PhysicsElementsHandler
|
||||||
|
@ -25,10 +27,10 @@ class SpriteManager:
|
||||||
for layer in DrawLayers.DRAW_ORDER:
|
for layer in DrawLayers.DRAW_ORDER:
|
||||||
self.layers[layer] = []
|
self.layers[layer] = []
|
||||||
|
|
||||||
def add_sprite(self, layer: str, sprite: UiElement):
|
def add_ui_element(self, layer: str, sprite: UiElement):
|
||||||
self.layers[layer].append(sprite)
|
self.layers[layer].append(sprite)
|
||||||
|
|
||||||
def remove_sprite(self, sprite: UiElement):
|
def remove_ui_element(self, sprite: UiElement):
|
||||||
for layer in self.layers:
|
for layer in self.layers:
|
||||||
if sprite in self.layers[layer]:
|
if sprite in self.layers[layer]:
|
||||||
self.layers[layer].remove(sprite)
|
self.layers[layer].remove(sprite)
|
||||||
|
@ -41,6 +43,17 @@ class SpriteManager:
|
||||||
for sprite in self.layers[layer]:
|
for sprite in self.layers[layer]:
|
||||||
sprite.draw(screen, screen_transform)
|
sprite.draw(screen, screen_transform)
|
||||||
|
|
||||||
|
def find_elements_at_position(self, position: tuple[float, float]) -> list[UiElement]:
|
||||||
|
elements = []
|
||||||
|
|
||||||
|
for layer in DrawLayers.DRAW_ORDER:
|
||||||
|
for sprite in self.layers[layer]:
|
||||||
|
bounding_box = sprite.get_bounding_box()
|
||||||
|
if bounding_box is not None and bounding_box.contains_point(position):
|
||||||
|
elements.append(sprite)
|
||||||
|
|
||||||
|
return elements
|
||||||
|
|
||||||
def collision_detected(self, sprite_a: Sprite, sprite_b: Sprite):
|
def collision_detected(self, sprite_a: Sprite, sprite_b: Sprite):
|
||||||
# print(f"Collision detected between {sprite_a} and {sprite_b}")
|
# print(f"Collision detected between {sprite_a} and {sprite_b}")
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
class BoundingBox:
|
class BoundingBox:
|
||||||
def __init__(self, x, y, width, height):
|
def __init__(self, x, y, width, height):
|
||||||
self.x = x
|
self.x = x
|
||||||
|
@ -12,5 +11,8 @@ class BoundingBox:
|
||||||
def get_position(self):
|
def get_position(self):
|
||||||
return self.x, self.y
|
return self.x, self.y
|
||||||
|
|
||||||
|
def contains_point(self, position: tuple[float, float]):
|
||||||
|
return self.x <= position[0] <= self.x + self.width and self.y <= position[1] <= self.y + self.height
|
||||||
|
|
||||||
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})"
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
from pygame.event import Event
|
||||||
|
|
||||||
|
|
||||||
|
class ClickEvent:
|
||||||
|
def __init__(self, world_position: tuple[float, float], screen_position: tuple[float, float], event: Event):
|
||||||
|
self.world_position = world_position
|
||||||
|
self.screen_position = screen_position
|
||||||
|
self.event = event
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"ClickEvent(world_position={self.world_position}, screen_position={self.screen_position})"
|
|
@ -4,6 +4,7 @@ from typing import Optional
|
||||||
import pygame
|
import pygame
|
||||||
from pygame import Surface
|
from pygame import Surface
|
||||||
|
|
||||||
|
from sprite.BoundingBox import BoundingBox
|
||||||
from sprite.PositionScale import PositionScale
|
from sprite.PositionScale import PositionScale
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,14 +12,29 @@ class UiElement:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.position_scale = PositionScale()
|
self.position_scale = PositionScale()
|
||||||
self.visible = True
|
self.visible = True
|
||||||
|
self.click_listeners = []
|
||||||
|
|
||||||
|
def add_click_listener(self, listener):
|
||||||
|
self.click_listeners.append(listener)
|
||||||
|
|
||||||
|
def remove_click_listener(self, listener):
|
||||||
|
self.click_listeners.remove(listener)
|
||||||
|
|
||||||
|
def click(self, click_event):
|
||||||
|
for listener in self.click_listeners:
|
||||||
|
listener(self, click_event)
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def tick(self, dt: float):
|
def tick(self, dt: float):
|
||||||
return
|
pass
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def render_sprite_image(self) -> Optional[Surface]:
|
def render_sprite_image(self) -> Optional[Surface]:
|
||||||
return None
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_bounding_box(self) -> BoundingBox:
|
||||||
|
pass
|
||||||
|
|
||||||
def draw(self, screen: pygame.Surface, screen_transform: PositionScale):
|
def draw(self, screen: pygame.Surface, screen_transform: PositionScale):
|
||||||
if not self.visible:
|
if not self.visible:
|
||||||
|
@ -27,20 +43,37 @@ class UiElement:
|
||||||
image = self.render_sprite_image()
|
image = self.render_sprite_image()
|
||||||
|
|
||||||
if image is not None:
|
if image is not None:
|
||||||
|
target_position = UiElement.transform_world_to_screen(self.position_scale.position, screen_transform)
|
||||||
|
|
||||||
screen_scale = screen_transform.scale
|
screen_scale = screen_transform.scale
|
||||||
screen_position = screen_transform.position
|
|
||||||
|
|
||||||
object_scale = self.position_scale.scale
|
object_scale = self.position_scale.scale
|
||||||
object_position = self.position_scale.position
|
|
||||||
|
|
||||||
target_position = ((object_position[0] + screen_position[0]) * screen_scale[0],
|
|
||||||
(object_position[1] + screen_position[1]) * screen_scale[1])
|
|
||||||
|
|
||||||
target_size = (int(screen_scale[0] * object_scale[0] * image.get_width()),
|
target_size = (int(screen_scale[0] * object_scale[0] * image.get_width()),
|
||||||
int(screen_scale[1] * object_scale[1] * image.get_height()))
|
int(screen_scale[1] * object_scale[1] * image.get_height()))
|
||||||
|
|
||||||
target_image = self.get_scaled_image(image, target_size)
|
target_image = UiElement.get_scaled_image(image, target_size)
|
||||||
screen.blit(target_image, target_position)
|
screen.blit(target_image, target_position)
|
||||||
|
|
||||||
def get_scaled_image(self, image, resize):
|
@staticmethod
|
||||||
|
def get_scaled_image(image, resize):
|
||||||
return pygame.transform.scale(image, resize)
|
return pygame.transform.scale(image, resize)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def transform_screen_to_world(convert_position: tuple, screen_transform: PositionScale):
|
||||||
|
screen_scale = screen_transform.scale
|
||||||
|
screen_position = screen_transform.position
|
||||||
|
|
||||||
|
object_position = (convert_position[0] / screen_scale[0] - screen_position[0],
|
||||||
|
convert_position[1] / screen_scale[1] - screen_position[1])
|
||||||
|
|
||||||
|
return object_position
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def transform_world_to_screen(convert_position: tuple, screen_transform: PositionScale):
|
||||||
|
screen_scale = screen_transform.scale
|
||||||
|
screen_position = screen_transform.position
|
||||||
|
|
||||||
|
object_position = (int((convert_position[0] + screen_position[0]) * screen_scale[0]),
|
||||||
|
(int(convert_position[1] + screen_position[1]) * screen_scale[1]))
|
||||||
|
|
||||||
|
return object_position
|
||||||
|
|
Loading…
Reference in New Issue