Introduced screen layouts and game states

main
Skyball2000 2023-03-29 10:45:48 +02:00
parent 2280e81629
commit e50d280afa
12 changed files with 311 additions and 207 deletions

View File

@ -5,13 +5,13 @@
#,#,S,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
#,#,S,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
#,#,S,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
#,#,S,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,L,,,,,,,,,,,,,,,,,,,,,,,,C,,,#
#,#,S,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,L,,,,,M,M,,,,,,,,,,,,,,,,,,C,,,#
#,#,S,,,,,,,,,,,,,,,,,,,+,+,+,+,S,S,S,S,S,+,+,+,+,+,S,S,S,S,S,+,+,+,+,+,S,S,S,S,S,+,+,+,+,+,+,+,+,+,#
#,#,S,,,,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#
#,#,S,,,,,,,,,,,,,,,,,,,,,#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
#,#,S,,,,,,,,,,,,,,,,,,,,,#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
#,#,,,,,,,,,,,,,,,,,,,,,,G,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
#,,,,,,,,,,,,,,,M,,,M,M,,M,,,,,,,,,,,,,,,,,M,,,,,,M,,,,,,,,,,,,,,,#
#,,,,,,,,,,,,,,,M,,,,,,M,,,,,,,,,,,,,,,,,M,,,,,,M,,,,,,,,,,,,,,,#
#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
#,,,,,,,,,,,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
#,L,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#

1 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
5 # # S #
6 # # S #
7 # # S #
8 # # S L M M C #
9 # # S + + + + S S S S S + + + + + S S S S S + + + + + S S S S S + + + + + + + + + #
10 # # S # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
11 # # S # #
12 # # S # #
13 # # G #
14 # M M M M M M #
15 # #
16 # + + + + + + + + + + + + + + + #
17 # L # # # # # # # # # # # # # # # #

View File

@ -15,7 +15,7 @@
},
{
"name": "level-02",
"theme": "castle",
"theme": "cave",
"abilities": [
],
"file": "level-02.csv"

View File

@ -834,5 +834,26 @@
"height": 36
}
]
},
{
"id": "ui_arrow",
"subsheets": [
{
"id": "left",
"delays": [
1
],
"width": 12,
"height": 12
},
{
"id": "right",
"delays": [
1
],
"width": 12,
"height": 12
}
]
}
]

View File

@ -25,3 +25,13 @@ class LevelManager:
filtered_row = list(map(lambda x: x.replace('\n', ''), split_row))
csv_array.append(filtered_row)
return csv_array
def get_levels_by_theme(self) -> dict[str, list[Level]]:
levels_by_theme = {}
for level in self.levels:
if level.theme not in levels_by_theme:
levels_by_theme[level.theme] = []
levels_by_theme[level.theme].append(level)
return levels_by_theme

View File

@ -1,7 +1,7 @@
import pygame
from level.Level import Level
from ui_elements.Background import Background
from ui_elements.ColoredRectangleUiElement import ColoredRectangleUiElement
from ui_elements.ClickEvent import ClickEvent
from ui_elements.TextLabel import TextLabel
@ -11,23 +11,27 @@ class LevelMenu:
def __init__(self, levels: [Level], x_pos: float, y_pos: float, width: float, height: float, column_count: int):
super().__init__()
self.levels = levels
self.x_pos = x_pos
self.y_pos = y_pos
self.level_select_listener = lambda selected_level: None
self.level_text_labels = []
self.width = width
self.height = height
self.column_count = column_count
self.position = (x_pos, y_pos)
self.create_level_text_labels()
self.background = Background(self.x_pos, self.y_pos, self.width, self.height, (100, 100, 100, 180))
self.background = ColoredRectangleUiElement(self.width, self.height, (100, 100, 100, 180))
self.background.position_scale.position = self.position
def create_level_text_labels(self):
current_text_label_x = self.x_pos
current_text_label_y = self.y_pos
current_text_label_position = [self.position[0], self.position[1]]
for index, level in enumerate(self.levels):
text_label = TextLabel(level.name, current_text_label_x, current_text_label_y, 60)
text_label = TextLabel(level.name,
current_text_label_position[0],
current_text_label_position[1],
60)
text_label.position_scale.scale = (0.5, 0.5)
text_label.add_click_listener(
lambda click_event, selected_level=level:
@ -36,10 +40,10 @@ class LevelMenu:
self.level_text_labels.append(text_label)
if (index + 1) % self.column_count == 0:
current_text_label_y += self.height / (len(self.levels) / self.column_count)
current_text_label_x = self.x_pos
current_text_label_position[0] = self.position[0]
current_text_label_position[1] += self.height / (len(self.levels) / self.column_count)
else:
current_text_label_x += self.width / self.column_count
current_text_label_position[0] += self.width / self.column_count
def handle_level_text_label_click(self, click_event: ClickEvent, level: Level):
if click_event.event.type == pygame.MOUSEBUTTONUP:

View File

@ -0,0 +1,16 @@
from abc import abstractmethod
from level.selection.ScreenManager import ScreenManager
from physics.SpriteManager import SpriteManager
from sprite.SpritesheetManager import SpritesheetManager
class LevelScreenManager(ScreenManager):
def __init__(self, sprite_manager: SpriteManager, spritesheet_manager: SpritesheetManager, main_loop):
super().__init__(sprite_manager, spritesheet_manager, main_loop)
def initialize(self):
pass
def destroy(self):
super().destroy()

View File

@ -0,0 +1,79 @@
from abc import abstractmethod
from level.Level import Level
from level.LevelManager import LevelManager
from level.selection.ScreenManager import ScreenManager
from physics.SpriteManager import SpriteManager, DrawLayers
from sprite.PositionScale import PositionScale
from sprite.SpritesheetManager import SpritesheetManager
from sprite.StaticSprite import StaticSprite
from ui_elements.ClickEvent import ClickEvent
from ui_elements.TextLabel import TextLabel
LABEL_COUNT = 10
OFFSET = (50, 20)
class LevelSelectionScreenManager(ScreenManager):
def __init__(self, sprite_manager: SpriteManager, spritesheet_manager: SpritesheetManager, main_loop,
level_manager: LevelManager):
super().__init__(sprite_manager, spritesheet_manager, main_loop)
self.level_manager = level_manager
self.levels_by_theme = self.level_manager.get_levels_by_theme()
self.themes = ['tutorial', 'cave', 'castle']
self.level_labels: list[TextLabel] = []
self.selected_theme = self.themes[0]
def select_next_theme(self, click: ClickEvent):
if click.is_click_down(ClickEvent.CLICK_LEFT):
index = (self.themes.index(self.selected_theme) + 1) % len(self.themes)
self.select_theme(self.themes[index])
def select_prev_theme(self, click):
if click.is_click_down(ClickEvent.CLICK_LEFT):
index = self.themes.index(self.selected_theme) - 1
if index < 0:
index = len(self.themes) - 1
self.select_theme(self.themes[index])
def select_level(self, click: ClickEvent, level: Level):
if click.is_click_down(ClickEvent.CLICK_LEFT):
self.main_loop.select_level(level)
def initialize(self):
arrow_left = StaticSprite(self.spritesheet_manager.get_sheet('ui_arrow'))
arrow_left.set_animation_state('left')
arrow_left.position_scale = PositionScale((OFFSET[0], OFFSET[1]), (3, 3))
arrow_left.add_click_listener(self.select_prev_theme)
self.add_element(DrawLayers.UI, arrow_left)
arrow_right = StaticSprite(self.spritesheet_manager.get_sheet('ui_arrow'))
arrow_right.set_animation_state('right')
arrow_right.position_scale = PositionScale((OFFSET[0] + 50, OFFSET[1]), (3, 3))
arrow_right.add_click_listener(self.select_next_theme)
self.add_element(DrawLayers.UI, arrow_right)
for i in range(0, LABEL_COUNT):
label = TextLabel(str(i), OFFSET[0] + 30, OFFSET[1] + 60 + i * 50, font_size=65)
label.position_scale.scale = (0.5, 0.5)
self.level_labels.append(label)
self.add_element(DrawLayers.UI, label)
self.select_theme(self.selected_theme)
def select_theme(self, theme: str):
self.selected_theme = theme
for label in self.level_labels:
label.set_text('')
label.click_listeners = []
for id, level in enumerate(self.levels_by_theme[self.selected_theme]):
self.level_labels[id].set_text(level.name)
self.level_labels[id].add_click_listener(lambda click: self.select_level(click, level))
def destroy(self):
super().destroy()

View File

@ -0,0 +1,16 @@
from abc import abstractmethod
from level.selection.ScreenManager import ScreenManager
from physics.SpriteManager import SpriteManager
from sprite.SpritesheetManager import SpritesheetManager
class MainMenuScreenManager(ScreenManager):
def __init__(self, sprite_manager: SpriteManager, spritesheet_manager: SpritesheetManager,main_loop):
super().__init__(sprite_manager, spritesheet_manager, main_loop)
def initialize(self):
pass
def destroy(self):
super().destroy()

View File

@ -0,0 +1,26 @@
from abc import abstractmethod
from physics.SpriteManager import SpriteManager
from sprite.SpritesheetManager import SpritesheetManager
from ui_elements.UiElement import UiElement
class ScreenManager:
def __init__(self, sprite_manager: SpriteManager, spritesheet_manager: SpritesheetManager, main_loop):
self.sprite_manager = sprite_manager
self.spritesheet_manager = spritesheet_manager
self.main_loop = main_loop
self.elements: list[UiElement] = []
def add_element(self, layer: str, ui_element: UiElement):
self.elements.append(ui_element)
self.sprite_manager.add_ui_element(layer, ui_element)
@abstractmethod
def initialize(self):
pass
def destroy(self):
for element in self.elements:
self.sprite_manager.remove_ui_element(element)

View File

@ -1,221 +1,146 @@
import random
from typing import Optional
import pygame
from level.Level import Level
from level.LevelManager import LevelManager
from level.elements.LoadedLevel import LoadedLevel
from level.selection.LevelScreenManager import LevelScreenManager
from level.selection.LevelSelectionScreenManager import LevelSelectionScreenManager
from level.selection.MainMenuScreenManager import MainMenuScreenManager
from level.selection.ScreenManager import ScreenManager
from physics import ConstantsParser
from physics.SpriteManager import SpriteManager, DrawLayers
from physics.SpriteManager import SpriteManager
from physics.TickData import TickData
from physics.sprites.PlayerSprite import PlayerSprite
from sprite.PositionScale import PositionScale
from sprite.Sprite import Sprite
from sprite.SpritesheetManager import SpritesheetManager
from sprite.StaticSprite import StaticSprite
from ui_elements.ClickEvent import ClickEvent
from ui_elements.KeyManager import KeyManager
from ui_elements.LevelMenu import LevelMenu
from ui_elements.TextLabel import TextLabel
what_to_run = 'level'
WIDTH = 12 * 71 * 1.5
HEIGHT = 12 * 40 * 1.5
DEPRECATED_WIDTH = 12 * 71 * 1.5
DEPRECATED_HEIGHT = 12 * 40 * 1.5
# Background to test for level design
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_tutorial = pygame.transform.scale(pygame.image.load('data/sprites/tutorial_bg.png'), (WIDTH, HEIGHT))
test_background_castle = pygame.transform.scale(pygame.image.load('data/sprites/castle_bg.png'),
(DEPRECATED_WIDTH, DEPRECATED_HEIGHT))
# test_background_castle = pygame.transform.scale(pygame.image.load('data/sprites/cave_bg.png'), (DEPRECATED_WIDTH, DEPRECATED_WIDTH))
# test_background_tutorial = pygame.transform.scale(pygame.image.load('data/sprites/tutorial_bg.png'), (DEPRECATED_WIDTH, DEPRECATED_WIDTH))
def apply_frame_rate(number: float):
def apply_frame_rate(number: float, frame_rate: float = 30):
"""
this function calculates a factor that will be multiplied with the
physics of the game to provide a constant speed
:param frame_rate: the frame rate of the game
:param number: The number to scale by the factor
:return: The scaled number
"""
return number / (frame_rate / 30)
if what_to_run == 'menu':
screen_transform = PositionScale((0, 0), (1.5, 1.5))
class MainLoop:
def __init__(self):
self.GAME_STATE_MENU = 'main_menu'
self.GAME_STATE_LEVEL_SELECTION = 'level_selection'
self.GAME_STATE_LEVEL = 'level'
screen_height = 12 * 40 * 1.5
screen_width = 12 * 71 * 1.5
self.screen_transform: PositionScale = PositionScale((0, 0), (1.5, 1.5))
self.window_size: tuple[float, float] = (1, 1)
pygame.init()
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("PM GAME")
clock = pygame.time.Clock()
frame_rate = 30
pygame.init()
pygame.display.set_caption("PM GAME")
spritesheet_manager = SpritesheetManager("data/sprites", "data/sprites/sprites.json")
sprite_manager = SpriteManager()
key_manager = KeyManager()
self.update_position_scale(self.screen_transform)
parsed_levels_manager = LevelManager('data/levels')
parsed_levels_manager.load_from_config('data/levels/levels.json')
self.screen = self.screen
self.clock = pygame.time.Clock()
self.frame_rate = 30
generated_level = LoadedLevel(sprite_manager, spritesheet_manager)
self.spritesheet_manager = SpritesheetManager("data/sprites", "data/sprites/sprites.json")
self.sprite_manager = SpriteManager()
self.key_manager = KeyManager()
level_menu_width = screen_width / 2
level_menu_height = screen_height / 2
level_menu = LevelMenu(parsed_levels_manager.levels, 0, 0, level_menu_width, level_menu_height, 3)
show_menu = False
self.parsed_levels_manager = LevelManager('data/levels')
self.parsed_levels_manager.load_from_config('data/levels/levels.json')
self.screen_manager: Optional[ScreenManager] = None
self.game_state = self.GAME_STATE_MENU
self.set_game_state(self.GAME_STATE_LEVEL_SELECTION)
def update_position_scale(self, position_scale: PositionScale):
self.screen_transform = position_scale
self.window_size = (
self.screen_transform.scale[0] * ConstantsParser.CONFIG.block_size[0] *
ConstantsParser.CONFIG.level_size[0],
self.screen_transform.scale[1] * ConstantsParser.CONFIG.block_size[1] *
ConstantsParser.CONFIG.level_size[1]
)
self.screen = pygame.display.set_mode((self.window_size[0], self.window_size[1]))
def select_level(self, level: Level):
print(level.name)
self.set_game_state(self.GAME_STATE_LEVEL)
def set_game_state(self, game_state: str):
self.game_state = game_state
if self.screen_manager is not None:
self.screen_manager.destroy()
if self.game_state == self.GAME_STATE_MENU:
self.screen_manager = MainMenuScreenManager(
self.sprite_manager, self.spritesheet_manager, self
)
elif self.game_state == self.GAME_STATE_LEVEL:
self.screen_manager = LevelScreenManager(
self.sprite_manager, self.spritesheet_manager, self
)
elif self.game_state == self.GAME_STATE_LEVEL_SELECTION:
self.screen_manager = LevelSelectionScreenManager(
self.sprite_manager, self.spritesheet_manager, self, self.parsed_levels_manager
)
else:
print('Invalid game state', self.game_state)
if self.screen_manager is not None:
self.screen_manager.initialize()
def main_loop(self):
while True:
self.clock.tick(self.frame_rate)
pygame_events: list[pygame.event.Event] = pygame.event.get()
self.key_manager.update_key_events(pygame_events)
click_events: list[ClickEvent] = ClickEvent.create_events(pygame_events, self.screen_transform)
for event in click_events:
for layer in self.sprite_manager.layers:
for sprite in self.sprite_manager.layers[layer]:
if sprite.get_bounding_box().contains_point(event.world_position):
sprite.click(event)
for event in pygame_events:
if event.type == pygame.QUIT:
pygame.quit()
quit()
self.screen.fill((0, 0, 0))
self.sprite_manager.tick(
TickData(apply_frame_rate(1, self.frame_rate),
pygame_events,
self.key_manager,
click_events,
self.screen_transform)
)
self.sprite_manager.draw(self.screen, self.screen_transform)
pygame.display.update()
def load_next_level(selected_level):
global show_menu
generated_level.destroy_level()
generated_level.load_level(selected_level)
destroy_menu()
show_menu = False
def destroy_menu():
global level_text_label
for level_text_label in level_menu.level_text_labels:
sprite_manager.remove_ui_element(level_text_label)
sprite_manager.remove_ui_element(level_menu.background)
level_menu.level_select_listener = lambda selected_level: load_next_level(selected_level)
while True:
clock.tick(frame_rate)
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 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):
sprite.click(event)
for event in pygame_events:
if event.type == pygame.QUIT:
pygame.quit()
quit()
if key_manager.is_keymap_down(KeyManager.KEY_ESCAPE):
if show_menu:
show_menu = False
destroy_menu()
else:
show_menu = True
sprite_manager.add_ui_element(DrawLayers.UI, level_menu.background)
for level_text_label in level_menu.level_text_labels:
sprite_manager.add_ui_element(DrawLayers.UI, level_text_label)
screen.fill((0, 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 == 'level':
screen_transform = PositionScale((0, 0), (1.5, 1.5))
pygame.init()
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]),
flags=pygame.HWSURFACE | pygame.DOUBLEBUF,
vsync=1,
depth=1)
pygame.display.set_caption("PM GAME")
clock = pygame.time.Clock()
frame_rate = 30
spritesheet_manager = SpritesheetManager("data/sprites", "data/sprites/sprites.json")
sprite_manager = SpriteManager()
key_manager = KeyManager()
parsed_levels_manager = LevelManager('data/levels')
parsed_levels_manager.load_from_config('data/levels/levels.json')
generated_level = LoadedLevel(sprite_manager, spritesheet_manager)
generated_level.load_level(parsed_levels_manager.levels[1])
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)
calculated_frame_rate_text = TextLabel("0 FPS", 2, 2, 70, alignment="left")
calculated_frame_rate_text.position_scale.scale = (0.3, 0.3)
sprite_manager.add_ui_element(DrawLayers.UI, calculated_frame_rate_text)
left_sprite = None
right_sprite = None
while True:
clock.tick(frame_rate)
calculated_frame_rate_text.text = f"{round(clock.get_fps())} FPS"
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()
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))
# Playground to test background on any level
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 == 'textlabel':
screen_transform = PositionScale((0, 0), (4, 4))
pygame.init()
screen = pygame.display.set_mode((800, 800))
pygame.display.set_caption("PM GAME")
clock = pygame.time.Clock()
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)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
screen.fill((0, 0, 110))
test1.draw(screen, screen_transform)
test2.draw(screen, screen_transform)
test3.draw(screen, screen_transform)
pygame.display.update()
main_loop: MainLoop = MainLoop()
main_loop.main_loop()

View File

@ -1,5 +1,7 @@
import math
import pygame
class BoundingBox:
def __init__(self, x, y, width, height):
@ -46,3 +48,6 @@ class BoundingBox:
"""
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
def get_rect(self) -> pygame.Rect:
return pygame.Rect(self.x, self.y, self.width, self.height)

View File

@ -8,24 +8,26 @@ from sprite.BoundingBox import BoundingBox
from ui_elements.UiElement import UiElement
class Background(UiElement):
class ColoredRectangleUiElement(UiElement):
def __init__(self, x_pos: float, y_pos: float, width: float, height: float, color: tuple):
def __init__(self, width: float, height: float, color: tuple):
super().__init__()
self.x_pos = x_pos
self.y_pos = y_pos
self.width = width
self.height = height
self.color = color
def tick(self, tick_data: TickData):
pass
def render_sprite_image(self) -> Optional[Surface]:
bounding_box = self.get_bounding_box()
background_rect = bounding_box.get_rect()
surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA)
background_rect = pygame.Rect(self.x_pos, self.y_pos, self.width, self.height)
pygame.draw.rect(surface, self.color, background_rect)
return surface
def get_bounding_box(self) -> BoundingBox:
return BoundingBox(-1, -1, 0, 0)
return BoundingBox(self.position_scale.position[0], self.position_scale.position[1],
self.width, self.height)
def tick(self, tick_data: TickData):
pass