Generalized UI Elements

main
Yan Wittmann 2023-03-26 09:51:11 +02:00
parent 8cbf623247
commit 72fd82c5a6
7 changed files with 157 additions and 67 deletions

View File

@ -1,2 +1,7 @@
# sep-se-platformer
Links:
- [doc: Software design](https://docs.google.com/document/d/1GpieqcuPjXC1P-a0mXGLe61XF8xzZ_h2taeojuUqToA/edit#heading=h.a6o0m6lt88hs)
- [tab: Arbeitspakete](https://docs.google.com/spreadsheets/d/1zyScof0CLPPSmdI0Dq3QWAdLAvyMhk_QFo7VJDFPymk/edit#gid=0)
- [tab: Levels](https://docs.google.com/spreadsheets/d/1kIfnlJLYZ0p-zY8Jl699yGgnn6wju2nfAVBx9WdDkQI/edit#gid=741912808)

View File

@ -4,6 +4,7 @@ import pygame
from pygame.font import Font
from level.LevelManager import LevelManager
from physics.SpriteManager import SpriteManager, DrawLayers
from sprite.DynamicSprite import DynamicSprite
from sprite.PositionScale import PositionScale
from sprite.SpritesheetManager import SpritesheetManager
@ -42,21 +43,24 @@ elif what_to_run == 'physics':
spritesheet_manager = SpritesheetManager("data/sprites", "data/sprites/sprites.json")
physics_handler = PhysicsElementsHandler()
sprite_manager = SpriteManager()
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)
sprite_manager.add_sprite(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)
physics_handler.add_sprite(test_3_sprite)
sprite_manager.add_sprite(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))
physics_handler.add_sprite(test_2_sprite)
sprite_manager.add_sprite(DrawLayers.OBJECTS, test_2_sprite)
text_1 = TextLabel("Das ist ein Test", 10, 80, 50, alignment="left")
text_1.position_scale.scale = (0.1, 0.1)
sprite_manager.add_sprite(DrawLayers.UI, text_1)
while True:
clock.tick(frame_rate)
@ -75,8 +79,8 @@ elif what_to_run == 'physics':
screen.fill((0, 0, 0))
physics_handler.tick(apply_frame_rate(1))
physics_handler.draw(screen, screen_transform)
sprite_manager.tick(apply_frame_rate(1))
sprite_manager.draw(screen, screen_transform)
pygame.display.update()
@ -88,9 +92,15 @@ elif what_to_run == 'textlabel':
pygame.display.set_caption("PM GAME")
clock = pygame.time.Clock()
test1 = TextLabel("Das ist ein Test", 400, 0, 50, alignment="left")
test2 = TextLabel("Das ist ein Test", 400, 10, 50, alignment="center")
test3 = TextLabel("Das ist ein Test", 400, 20, 50, alignment="right")
test1 = TextLabel("Das ist ein Test", 100, 0, 50, alignment="left")
test2 = TextLabel("Das ist ein Test", 100, 50, 50, alignment="left")
test3 = TextLabel("Das ist ein Test", 100, 20, 50, alignment="left")
test1.position_scale.scale = (0.2, 0.2)
test2.position_scale.scale = (0.4, 0.4)
test3.position_scale.scale = (0.2, 0.2)
test3.visible = False
while True:
clock.tick(5)

View File

@ -4,6 +4,7 @@ from sprite.DynamicSprite import DynamicSprite
from sprite.PositionScale import PositionScale
from sprite.Sprite import Sprite
from sprite.StaticSprite import StaticSprite
from ui_elements.UiElement import UiElement
MOTION_STEPS = 10
TOLERANCE = 1
@ -11,16 +12,14 @@ TOLERANCE = 1
class PhysicsElementsHandler:
def __init__(self):
self.sprites: list[Sprite] = []
pass
def add_sprite(self, sprite: Sprite):
self.sprites.append(sprite)
def tick_all_layers(self, dt: float, layers: dict[str, list[UiElement]]):
for layer in layers:
self.tick(dt, layers[layer])
def remove_sprite(self, sprite: Sprite):
self.sprites.remove(sprite)
def tick(self, dt: float):
for sprite in self.sprites:
def tick(self, dt: float, sprites: list[UiElement]):
for sprite in sprites:
sprite.tick(dt)
# handle motion and collisions. To do this:
@ -34,9 +33,9 @@ class PhysicsElementsHandler:
# 4.2.2. If it does, move the sprite back to the previous position and stop the motion
# 4.2.3. If it doesn't, move the sprite to the new position
colliders = [sprite for sprite in self.sprites if sprite.is_collider and isinstance(sprite, StaticSprite)]
colliders = [sprite for sprite in sprites if isinstance(sprite, StaticSprite) and sprite.is_collider]
dynamic_sprites = [sprite for sprite in self.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])
for sprite in sorted_dynamic_sprites:
@ -97,7 +96,3 @@ class PhysicsElementsHandler:
return True
return False
def draw(self, screen: Surface, screen_transform: PositionScale):
for sprite in self.sprites:
sprite.draw(screen, screen_transform)

View File

@ -0,0 +1,41 @@
from pygame import Surface
from physics.PhysicsElementsHandler import PhysicsElementsHandler
from sprite.PositionScale import PositionScale
from sprite.Sprite import Sprite
from ui_elements.UiElement import UiElement
class DrawLayers:
BACKGROUND = 'background'
LEVEL = 'level'
OBJECTS = 'objects'
PLAYER = 'player'
UI = 'ui'
DRAW_ORDER = [BACKGROUND, LEVEL, OBJECTS, PLAYER, UI]
class SpriteManager:
def __init__(self):
self.physics_handler = PhysicsElementsHandler()
self.layers: dict[str, list[UiElement]] = {}
for layer in DrawLayers.DRAW_ORDER:
self.layers[layer] = []
def add_sprite(self, layer: str, sprite: UiElement):
self.layers[layer].append(sprite)
def remove_sprite(self, sprite: UiElement):
for layer in self.layers:
if sprite in self.layers[layer]:
self.layers[layer].remove(sprite)
def tick(self, dt: float):
self.physics_handler.tick_all_layers(dt, self.layers)
def draw(self, screen: Surface, screen_transform: PositionScale):
for layer in DrawLayers.DRAW_ORDER:
for sprite in self.layers[layer]:
sprite.draw(screen, screen_transform)

View File

@ -4,21 +4,20 @@ import sys
from sprite.BoundingBox import BoundingBox
from sprite.PositionScale import PositionScale
from sprite.Spritesheet import Spritesheet
from ui_elements.UiElement import UiElement
sys.path.append('./sprite')
class Sprite:
class Sprite(UiElement):
def __init__(self, spritesheet: Spritesheet):
super().__init__()
self.spritesheet = spritesheet
self.animation_state = list(self.spritesheet.animations.keys())[0]
self.animation_frame = 0
self.animation_delay = 0
self.position_scale = PositionScale()
self.visible = True
self.animated = True
self.image = None
@ -47,22 +46,8 @@ class Sprite:
self.animation_frame = frame
self.tick(0)
def draw(self, screen: pygame.Surface, screen_transform: PositionScale):
if not self.visible:
return
if self.image is not None:
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 render_sprite_image(self) -> pygame.Surface:
return self.image
def get_bounding_box(self) -> BoundingBox:
return BoundingBox(
@ -72,9 +57,6 @@ class Sprite:
self.image.get_height() * self.position_scale.scale[1]
)
def get_scaled_image(self, image, resize):
return pygame.transform.scale(image, resize)
def dump(self, file):
# re-attach all the images to a single surface and save it to a file
width = 0

View File

@ -3,20 +3,29 @@ from pygame import Surface, Rect
from pygame.font import Font
from sprite.PositionScale import PositionScale
from ui_elements.UiElement import UiElement
class TextLabel:
def __init__(self, text: str, x_position: float, y_position: float, font_size: int,
alignment: str = "left"):
class TextLabel(UiElement):
def tick(self, dt: float):
pass
def __init__(self, text: str, x_position: float, y_position: float, font_size: int, alignment: str = "left"):
super().__init__()
self.text = text
self.x_position = x_position
self.y_position = y_position
self.alignment = alignment
self.current_width = 0
self.current_height = 0
self.alignment = alignment
self.font_size = font_size
self.font = Font('data/font/MilkyNice.ttf', self.font_size)
self.position_scale = PositionScale()
def render_sprite_image(self) -> pygame.Surface:
return self.font.render(str(self.text), True, pygame.Color('white'))
def draw(self, screen: Surface, screen_transform: PositionScale):
rendered_font = self.font.render(str(self.text), True, pygame.Color('white'))
@ -25,23 +34,13 @@ class TextLabel:
self.current_height = rendered_font.get_height()
if self.alignment == "right":
screen.blit(rendered_font, (self.x_position - self.current_width, self.y_position))
self.position_scale.position = (self.x_position - self.current_width, self.y_position)
elif self.alignment == "left":
screen.blit(rendered_font, (self.x_position, self.y_position))
self.position_scale.position = (self.x_position, self.y_position)
elif self.alignment == "center":
screen.blit(rendered_font, (self.x_position - self.current_width / 2, self.y_position))
self.position_scale.position = (self.x_position - self.current_width / 2, self.y_position)
target_scale = (screen_transform.scale[0] * self.position_scale.scale[0],
screen_transform.scale[1] * self.position_scale.scale[1])
super().draw(screen, screen_transform)
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.x_position),
int(target_scale[1] * self.y_position))
target_image = pygame.transform.scale(rendered_font, target_size)
def set_text(self, new_text: str):
self.text = new_text
def set_text(self, new_text: str):
self.text = new_text

View File

@ -0,0 +1,58 @@
import abc
from typing import Optional
import pygame
from pygame import Surface
from sprite.PositionScale import PositionScale
class UiElement:
def __init__(self):
self.position_scale = PositionScale()
self.visible = True
@abc.abstractmethod
def tick(self, dt: float):
return
@abc.abstractmethod
def render_sprite_image(self) -> Optional[Surface]:
return None
def draw(self, screen: pygame.Surface, screen_transform: PositionScale):
if not self.visible:
return
image = self.render_sprite_image()
if image is not None:
# 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] * image.get_width()),
# int(target_scale[1] * image.get_height()))
# target_image = self.get_scaled_image(image, target_size)
# screen.blit(target_image, target_position)
screen_scale = screen_transform.scale
screen_position = screen_transform.position
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()),
int(screen_scale[1] * object_scale[1] * image.get_height()))
target_image = self.get_scaled_image(image, target_size)
screen.blit(target_image, target_position)
def get_scaled_image(self, image, resize):
return pygame.transform.scale(image, resize)