def applied_on_entity(self, entity): self.entity = entity entity.event_manager.bind(UpdateEvent, self.update) entity.event_manager.bind(HoverEvent, self.hover_event) entity.event_manager.bind(ClickEvent, self.click_event) if self.text is not None: trans = entity.get_component(TransformComponent) size = entity.get_component(RendererComponent).size self.label = Entity() entity.scene.add_entity(self.label) self.label_text = TextRenderer(self.text, 20, Colors.white) self.label.add_component( PyGameRendererComponent(self.label_text, (100, 100))) ls = self.label_text.render(entity, None).get_size() self.label.add_component( TransformComponent( Vector2(trans.x + size[0] / 2 - ls[0] / 2, trans.y + size[1] / 2 - ls[1] / 2)))
def add_grain(self, rect, size, big=False): grain = Entity() self.add_entity(grain) grain.add_component( TransformComponent( Vector2(rect.x + rect.w / 2 - size / 2, rect.y + rect.h / 2 - size / 2))) grain.add_component( RendererComponent(ImageRenderer("./assets/images/small_grain.png"), (size, size))) grain.add_component(NameComponent('grain' + ('big' if big else '')))
def create_objects(self): self.field_obj = Field(self.game, 32) self.ghost_obj = GhostMove(self.game, self.field_obj) field = Entity() self.add_entity(field) shader = Shader.init_from_files("assets/shaders/walls/walls.vert", "assets/shaders/walls/walls.frag") field.add_component(TransformComponent(Vector2(0, 0))) field.add_component( PyGameRendererComponent(ObjectRenderer(self.field_obj), self.game.screen_size, True, shader))
def __init__(self, game): super().__init__(game) self.objects = [] self.create_objects() self.rederer = SceneRenderer() rend_entity = Entity() self.add_entity(rend_entity) rend_entity.add_component(TransformComponent(Vector2(0, 0))) rend_entity.add_component(PyGameRendererComponent(self.rederer, game.screen_size)) self.add_entities()
def start(): pygame.init() size = width, height = (640, 480) game = Game() game.setup_default_components(size) scene = game.scene background = Entity() scene.add_entity(background) background.add_component(TransformComponent(Vector2(0, 0))) background.add_component( RendererComponent(RectRenderer(Colors.from_hex("#4C5B5C")), size)) menu_label = Entity() scene.add_entity(menu_label) menu_label.add_component( TransformComponent(Vector2(width / 2 - 120, 40))) menu_label.add_component( PyGameRendererComponent( TextRenderer("Simple Menu", 40, Colors.white), (100, 100))) click_label = Entity() scene.add_entity(click_label) click_label.add_component( TransformComponent(Vector2(width / 2 - 60, 90))) click_label_text = TextRenderer("Click count: 0", 20, Colors.white) click_label.add_component( PyGameRendererComponent(click_label_text, (100, 100))) play_button = Entity() scene.add_entity(play_button) play_button.add_component( TransformComponent(Vector2(width / 2 - 100, 150))) shader = Shader.init_from_files("assets/button_shader.vert", "assets/button_shader.frag") play_button.add_component( RendererComponent(RectRenderer(Colors.from_hex("#4F6D7A")), (200, 50), shader)) play_button.add_component(BoxCollider((200, 50))) play_button.add_component(MouseColliderComponent()) play_button.add_component( ButtonComponent(shader, text="Play", hover_color=Colors.from_hex("#3891A6"), click_color=Colors.from_hex("#FDE74C"))) exit_button = Entity() scene.add_entity(exit_button) exit_button.add_component( TransformComponent(Vector2(width / 2 - 100, 250))) shader1 = Shader.init_from_files("assets/button_shader.vert", "assets/button_shader.frag") exit_button.add_component( RendererComponent(RectRenderer(Colors.from_hex("#9E3A35")), (200, 50), shader1)) exit_button.add_component(BoxCollider((200, 50))) exit_button.add_component(MouseColliderComponent()) exit_button.add_component( ButtonComponent(shader1, text="Exit", hover_color=Colors.from_hex("#C24741"), click_color=Colors.from_hex("#FDE74C"))) def press(event): if event.entity == play_button: click_label_text.text = "Click count: {}".format( int(click_label_text.text.split(": ")[1]) + 1) elif event.entity == exit_button: game.exit() exit_button.event_manager.bind(ButtonPressEvent, press) game.add_component(ExitOnEscape()) game.run()
def game_over(self): print("game over") self.entity.event_manager.trigger_event( PlaySoundEvent(Sounds.DEATH, 0, 20)) scene = self.entity.scene black_back = Entity() scene.add_entity(black_back) black_back.add_component(TransformComponent(Vector2(0, 0))) shader = Shader.init_from_files("assets/shaders/back/back.vert", "assets/shaders/back/back.frag") shader.inject_rect = False shader.uniform_alpha = 1.0 black_back.add_component( RendererComponent(RectRenderer(Colors.black), self.entity.screen_size, shader)) black_back.add_component( AnimationComponent( step=3, end=100, function=lambda x: shader.set_uniform("alpha", x / 100.0))) game_text = Entity() scene.add_entity(game_text) game_transform = TransformComponent( Vector2(0, self.entity.height / 2 - 65)) game_text.add_component(game_transform) game_text.add_component( PyGameRendererComponent( TextRenderer("Game", font_size=24, color=Colors.white, font="assets/fonts/Emulogic.ttf"), (0, 0))) game_text.add_component( AnimationComponent(step=10, end=self.entity.width / 2 - 220, function=lambda x: game_transform.position. update(x, game_transform.y))) over_text = Entity() scene.add_entity(over_text) over_transform = TransformComponent( Vector2(0, self.entity.height / 2 - 65)) over_text.add_component(over_transform) over_text.add_component( PyGameRendererComponent( TextRenderer("Over", font_size=24, color=Colors.white, font="assets/fonts/Emulogic.ttf"), (0, 0))) over_text.add_component( AnimationComponent(step=-22, start=self.entity.width, end=self.entity.width / 2 - 110, function=lambda x: over_transform.position. update(x, game_transform.y))) self.entity.add_component( AnimationComponent(end=80, completion=self.go_to_menu))
class ButtonComponent(Component): def __init__(self, shader, text=None, hover_color=Colors.blue, click_color=Colors.green): self.entity = None self.hovering = False self.click = False self.state = ButtonState.Normal self.hover_color = hover_color self.click_color = click_color self.text = text self.click_position = (-1.0, -1.0) shader.uniform_click_pos = self.click_position shader.uniform_hover_color = hover_color.to_float() shader.uniform_click_color = click_color.to_float() shader.uniform_hover_progress = 0.0 shader.uniform_click_progress = 0.0 self.shader = shader self.label = None self.label_text = None def create_hover_animation(self, reverse=False): return AnimationComponent(step=-10 if reverse else 10, start=120 if reverse else 0, end=0 if reverse else 220, function=lambda x: self.shader.set_uniform( "hover_progress", x / 100.0)) def create_click_animation(self, reverse=False): return AnimationComponent(step=-2 if reverse else 10, start=120 if reverse else 0, end=0 if reverse else 120, function=lambda x: self.shader.set_uniform( "click_progress", x / 100.0)) def start_animation(self, anim): if self.entity.contains_component(AnimationComponent): self.entity.remove_component(AnimationComponent) self.entity.add_component(anim) def update(self, event): if self.hovering and self.state == ButtonState.Normal: self.start_animation(self.create_hover_animation(False)) self.state = ButtonState.Hovered elif not self.hovering and self.state == ButtonState.Hovered: self.start_animation(self.create_hover_animation(True)) self.state = ButtonState.Normal elif not self.hovering and self.state == ButtonState.Clicked: self.shader.uniform_click_pos = (-1.0, -1.0) self.shader.uniform_hover_progress = 0.0 self.state = ButtonState.Normal elif self.click and not self.entity.contains_component( AnimationComponent): height = self.entity.scene.game.screen_size[1] self.shader.uniform_click_pos = (self.click_position[0], height - self.click_position[1]) self.start_animation(self.create_click_animation(False)) self.state = ButtonState.Clicked self.entity.event_manager.trigger_event( ButtonPressEvent(self.entity)) self.hovering = False self.click = False def hover_event(self, event): if event.entity == self.entity: self.hovering = True def click_event(self, event): if event.entity == self.entity and event.buttons[0]: self.click = True self.click_position = tuple([float(i) for i in event.position]) def applied_on_entity(self, entity): self.entity = entity entity.event_manager.bind(UpdateEvent, self.update) entity.event_manager.bind(HoverEvent, self.hover_event) entity.event_manager.bind(ClickEvent, self.click_event) if self.text is not None: trans = entity.get_component(TransformComponent) size = entity.get_component(RendererComponent).size self.label = Entity() entity.scene.add_entity(self.label) self.label_text = TextRenderer(self.text, 20, Colors.white) self.label.add_component( PyGameRendererComponent(self.label_text, (100, 100))) ls = self.label_text.render(entity, None).get_size() self.label.add_component( TransformComponent( Vector2(trans.x + size[0] / 2 - ls[0] / 2, trans.y + size[1] / 2 - ls[1] / 2)))
def start(): pygame.init() pygame.display.set_caption('PyDungeon') size = (640, 480) game = Game() game.setup_default_components(size) scene = game.scene tilemap = Entity() scene.add_entity(tilemap) tilemap.add_component(NameComponent("tilemap")) tilemap.add_component(TransformComponent(Vector2(0, 0))) ts = TileSet() ts.load("./assets/tileset.png", "./assets/tileinfo.info") tm = TileMap(ts, size) mp = [[["floor_" + str(random.randrange(1, 8))] for _ in range(24)] for _ in range(24)] PyDungeons.build_wall_rect(mp, pygame.Rect(2, 2, 10, 10)) tm.load_letters(mp) tilemap.add_component(tm) tilemap.add_component( TileMapCollider( tm, ["wall_mid", "wall_side_mid_right", "wall_side_mid_left"])) tilemap.add_component(RendererComponent(TileMapRenderer(), size)) player = Entity() scene.add_entity(player) player.add_component(NameComponent("player")) key_bindings = [[pygame.K_a], [pygame.K_d], [pygame.K_w], [pygame.K_s]] player.add_component(MoveComponent(1, 2)) player.add_component(KeyControlComponent(key_bindings)) player.add_component(TransformComponent(Vector2(100, 100))) player.add_component(BoxCollider((16 * 2, 22 * 2), Vector2(0, 12))) player.add_component( RendererComponent(TileRenderer(ts.tiles["knight_f_idle_anim"], ts), (16 * 2, 28 * 2))) game.add_component(ExitOnEscape()) game.run()
def add_entities(self): grain_size = 8 for g in self.field_obj.get_cells_by_type(Floor, Meta.grain_small): self.game_over.max_grains_count += 1 self.add_grain(g.rect, grain_size) big_grain_size = 16 for g in self.field_obj.get_cells_by_type(Floor, Meta.grain_big): self.game_over.max_grains_count += 1 self.add_grain(g.rect, big_grain_size, True) player = Entity() ghost_pos = self.field_obj.get_cells_by_type(Floor, Meta.ghost_spawn) self.pinky = Entity() self.add_entity(self.pinky) self.pinky.add_component( TransformComponent(Vector2(*ghost_pos[0].rect.xy))) self.pinky.add_component(BoxCollider((32, 32))) self.pinky.add_component( RendererComponent(ImageRenderer("assets/images/ghosts/pink.png"), (32, 32))) self.pinky.add_component( GhostMoveComponent(self.field_obj, 2, self.pinky_find_target, player, Colors.pink)) self.pinky.add_component(ScaryModeComponent()) red = Entity() self.add_entity(red) red.add_component(TransformComponent(Vector2(*ghost_pos[1].rect.xy))) red.add_component(BoxCollider((32, 32))) red.add_component( RendererComponent(ImageRenderer("assets/images/ghosts/red.png"), (32, 32))) red.add_component( GhostMoveComponent(self.field_obj, 2, self.red_find_target, player, Colors.red)) red.add_component(ScaryModeComponent()) inky = Entity() self.add_entity(inky) inky.add_component(TransformComponent(Vector2(*ghost_pos[2].rect.xy))) inky.add_component(BoxCollider((32, 32))) inky.add_component( RendererComponent(ImageRenderer("assets/images/ghosts/blue.png"), (32, 32))) inky.add_component( GhostMoveComponent(self.field_obj, 2, self.inky_find_target, player, Colors.purple)) inky.add_component(ScaryModeComponent()) clyde = Entity() self.add_entity(clyde) clyde.add_component(TransformComponent(Vector2(*ghost_pos[3].rect.xy))) clyde.add_component(BoxCollider((32, 32))) clyde.add_component( RendererComponent(ImageRenderer("assets/images/ghosts/orange.png"), (32, 32))) clyde.add_component( GhostMoveComponent(self.field_obj, 2, self.clyde_find_target, player, Colors.orange)) clyde.add_component(ScaryModeComponent()) debug_line = Entity() self.add_entity(debug_line) debug_line.add_component(TransformComponent(Vector2(0, 0))) debug_line.add_component(RendererComponent(LineRenderer(), (0, 0))) debug_line.add_component(LineHandlerComponent()) score_label = Entity() self.add_entity(score_label) score_label.add_component( TransformComponent(Vector2(self.game.width - 200, 0))) score_label.add_component( PyGameRendererComponent( TextRenderer("score", font_size=18, color=Colors.white, font="assets/fonts/Emulogic.ttf"), (0, 0))) score = Entity() self.add_entity(score) score.add_component( TransformComponent(Vector2(self.game.width - 200, 20))) score.add_component( PyGameRendererComponent( TextRenderer("000", font_size=18, color=Colors.white, font="assets/fonts/Emulogic.ttf"), (0, 0))) score.add_component(ScoreIncreaserComponent()) high_score_label = Entity() self.add_entity(high_score_label) high_score_label.add_component( TransformComponent(Vector2(self.game.width - 200, 50))) high_score_label.add_component( PyGameRendererComponent( TextRenderer("high score", font_size=18, color=Colors.white, font="assets/fonts/Emulogic.ttf"), (0, 0))) high_score_num = self.scoreboard.get_instance(0)[1] high_score = Entity() self.add_entity(high_score) high_score.add_component( TransformComponent(Vector2(self.game.width - 200, 70))) high_score.add_component( PyGameRendererComponent( TextRenderer(str(high_score_num), font_size=18, color=Colors.white, font="assets/fonts/Emulogic.ttf"), (0, 0))) ts = TileSet() ts.load("./assets/tilesets/pacman_tiles.png", "./assets/tilesets/pacman.info") for i in range(3): pacman_live = Entity() self.add_entity(pacman_live) pacman_live.add_component( TransformComponent( Vector2(self.game.width - 200 + 34 * i, self.game.height - 100))) pacman_live.add_component( RendererComponent( TileRenderer(ts.tiles["pacman"], ts, animate=False), (32, 32))) self.add_entity(player) key_bindings = [[pygame.K_a], [pygame.K_d], [pygame.K_w], [pygame.K_s]] pacman_pos = self.field_obj.get_cells_by_type( Floor, Meta.pacman_spawn)[0].rect.xy player.add_component(MoveComponent(2)) player.add_component(PacmanCollisions(self.field_obj)) player.add_component(KeyControlComponent(key_bindings, MoveComponent)) player.add_component(TransformComponent(Vector2(*pacman_pos))) player.add_component(BoxCollider((32, 32))) player.add_component( RendererComponent( TileRenderer(ts.tiles["pacman"], ts, animation_speed=0.3), (32, 32))) player.add_component(GrainCollisions()) player.add_component(GhostCollision([red, self.pinky, inky, clyde]))
class MainScene(Scene): def __init__(self, game): self.game_over = GameOverComponent() game.add_component(self.game_over) game.add_component(MusicPlayerComponent()) self.scoreboard = ScoreBoard() self.ghost_obj = None self.field_obj = None self.pinky = None super().__init__(game) def create_objects(self): self.field_obj = Field(self.game, 32) self.ghost_obj = GhostMove(self.game, self.field_obj) field = Entity() self.add_entity(field) shader = Shader.init_from_files("assets/shaders/walls/walls.vert", "assets/shaders/walls/walls.frag") field.add_component(TransformComponent(Vector2(0, 0))) field.add_component( PyGameRendererComponent(ObjectRenderer(self.field_obj), self.game.screen_size, True, shader)) def add_grain(self, rect, size, big=False): grain = Entity() self.add_entity(grain) grain.add_component( TransformComponent( Vector2(rect.x + rect.w / 2 - size / 2, rect.y + rect.h / 2 - size / 2))) grain.add_component( RendererComponent(ImageRenderer("./assets/images/small_grain.png"), (size, size))) grain.add_component(NameComponent('grain' + ('big' if big else ''))) def removed(self): super().removed() self.game.remove_component(GameOverComponent) self.game.remove_component(MusicPlayerComponent) def add_entities(self): grain_size = 8 for g in self.field_obj.get_cells_by_type(Floor, Meta.grain_small): self.game_over.max_grains_count += 1 self.add_grain(g.rect, grain_size) big_grain_size = 16 for g in self.field_obj.get_cells_by_type(Floor, Meta.grain_big): self.game_over.max_grains_count += 1 self.add_grain(g.rect, big_grain_size, True) player = Entity() ghost_pos = self.field_obj.get_cells_by_type(Floor, Meta.ghost_spawn) self.pinky = Entity() self.add_entity(self.pinky) self.pinky.add_component( TransformComponent(Vector2(*ghost_pos[0].rect.xy))) self.pinky.add_component(BoxCollider((32, 32))) self.pinky.add_component( RendererComponent(ImageRenderer("assets/images/ghosts/pink.png"), (32, 32))) self.pinky.add_component( GhostMoveComponent(self.field_obj, 2, self.pinky_find_target, player, Colors.pink)) self.pinky.add_component(ScaryModeComponent()) red = Entity() self.add_entity(red) red.add_component(TransformComponent(Vector2(*ghost_pos[1].rect.xy))) red.add_component(BoxCollider((32, 32))) red.add_component( RendererComponent(ImageRenderer("assets/images/ghosts/red.png"), (32, 32))) red.add_component( GhostMoveComponent(self.field_obj, 2, self.red_find_target, player, Colors.red)) red.add_component(ScaryModeComponent()) inky = Entity() self.add_entity(inky) inky.add_component(TransformComponent(Vector2(*ghost_pos[2].rect.xy))) inky.add_component(BoxCollider((32, 32))) inky.add_component( RendererComponent(ImageRenderer("assets/images/ghosts/blue.png"), (32, 32))) inky.add_component( GhostMoveComponent(self.field_obj, 2, self.inky_find_target, player, Colors.purple)) inky.add_component(ScaryModeComponent()) clyde = Entity() self.add_entity(clyde) clyde.add_component(TransformComponent(Vector2(*ghost_pos[3].rect.xy))) clyde.add_component(BoxCollider((32, 32))) clyde.add_component( RendererComponent(ImageRenderer("assets/images/ghosts/orange.png"), (32, 32))) clyde.add_component( GhostMoveComponent(self.field_obj, 2, self.clyde_find_target, player, Colors.orange)) clyde.add_component(ScaryModeComponent()) debug_line = Entity() self.add_entity(debug_line) debug_line.add_component(TransformComponent(Vector2(0, 0))) debug_line.add_component(RendererComponent(LineRenderer(), (0, 0))) debug_line.add_component(LineHandlerComponent()) score_label = Entity() self.add_entity(score_label) score_label.add_component( TransformComponent(Vector2(self.game.width - 200, 0))) score_label.add_component( PyGameRendererComponent( TextRenderer("score", font_size=18, color=Colors.white, font="assets/fonts/Emulogic.ttf"), (0, 0))) score = Entity() self.add_entity(score) score.add_component( TransformComponent(Vector2(self.game.width - 200, 20))) score.add_component( PyGameRendererComponent( TextRenderer("000", font_size=18, color=Colors.white, font="assets/fonts/Emulogic.ttf"), (0, 0))) score.add_component(ScoreIncreaserComponent()) high_score_label = Entity() self.add_entity(high_score_label) high_score_label.add_component( TransformComponent(Vector2(self.game.width - 200, 50))) high_score_label.add_component( PyGameRendererComponent( TextRenderer("high score", font_size=18, color=Colors.white, font="assets/fonts/Emulogic.ttf"), (0, 0))) high_score_num = self.scoreboard.get_instance(0)[1] high_score = Entity() self.add_entity(high_score) high_score.add_component( TransformComponent(Vector2(self.game.width - 200, 70))) high_score.add_component( PyGameRendererComponent( TextRenderer(str(high_score_num), font_size=18, color=Colors.white, font="assets/fonts/Emulogic.ttf"), (0, 0))) ts = TileSet() ts.load("./assets/tilesets/pacman_tiles.png", "./assets/tilesets/pacman.info") for i in range(3): pacman_live = Entity() self.add_entity(pacman_live) pacman_live.add_component( TransformComponent( Vector2(self.game.width - 200 + 34 * i, self.game.height - 100))) pacman_live.add_component( RendererComponent( TileRenderer(ts.tiles["pacman"], ts, animate=False), (32, 32))) self.add_entity(player) key_bindings = [[pygame.K_a], [pygame.K_d], [pygame.K_w], [pygame.K_s]] pacman_pos = self.field_obj.get_cells_by_type( Floor, Meta.pacman_spawn)[0].rect.xy player.add_component(MoveComponent(2)) player.add_component(PacmanCollisions(self.field_obj)) player.add_component(KeyControlComponent(key_bindings, MoveComponent)) player.add_component(TransformComponent(Vector2(*pacman_pos))) player.add_component(BoxCollider((32, 32))) player.add_component( RendererComponent( TileRenderer(ts.tiles["pacman"], ts, animation_speed=0.3), (32, 32))) player.add_component(GrainCollisions()) player.add_component(GhostCollision([red, self.pinky, inky, clyde])) @staticmethod def red_find_target(pacman, field, pos): return pacman.get_component(TransformComponent).pos @staticmethod def clyde_find_target(pacman, field, pos): pac_pos = pacman.get_component(TransformComponent).pos if pac_pos.distance_to(pos) > field.size * 8: return pac_pos return Vector2(0, len(field.matrix) * field.size) @staticmethod def vec2tuple(vec): return int(vec.x), int(vec.y) def find_cell_ahead(self, pac_pos, dir, field, distance): pdir = Vector2(dir.x, dir.y) start_pos = Vector2(pac_pos.x // field.size, pac_pos.y // field.size) cell_pos = Vector2(start_pos.x, start_pos.y) c_dirs = [Vector2(1, 0), Vector2(-1, 0), Vector2(0, 1), Vector2(0, -1)] for i in range(distance): cell = field.get_cell_iter(*self.vec2tuple(cell_pos)) next_cell = field.get_cell_iter(*self.vec2tuple(cell_pos + dir)) if (Meta.ghost_turn in cell.meta and not next_cell.state) or not cell.state: for c_dir in c_dirs: new_pos = cell_pos + c_dir if start_pos != cell_pos + c_dir and (c_dir.x != -pdir.x or c_dir.y != -pdir.y) \ and field.get_cell_iter(*self.vec2tuple(new_pos)).state: dir = c_dir cell_pos += dir return cell_pos def pinky_find_target(self, pacman, field, pos): pac_pos = pacman.get_component(TransformComponent).pos dir = pacman.get_component(MoveComponent).direction cell_pos = self.find_cell_ahead(pac_pos, dir, field, 4) res_pos = cell_pos * field.size if pac_pos.distance_to(pos) < res_pos.distance_to( pos) and pac_pos.distance_to(pos) < 128: res_pos = pac_pos pacman.event_manager.trigger_event( DrawDebugLineEvent(self.create_rect_path(res_pos + Vector2(16, 16)), color=Colors.green)) return res_pos def inky_find_target(self, pacman, field, pos): pac_pos = pacman.get_component(TransformComponent).pos dir = pacman.get_component(MoveComponent).direction cell_pos = self.find_cell_ahead(pac_pos, dir, field, 2) * field.size pinky_pos = self.pinky.get_component(TransformComponent).pos rs = cell_pos * 2 - pinky_pos dst = cell_pos.distance_to(pinky_pos) for i in range(int(dst // field.size // 2)): cell = field.get_cell( rs.lerp(cell_pos, 0.5 + (i * field.size) / dst)) if cell.state: rs = Vector2(*cell.rect.xy) break cell = field.get_cell( rs.lerp(cell_pos, 0.5 - (i * field.size) / dst)) if cell.state: rs = Vector2(*cell.rect.xy) break res_pos = rs if not field.get_cell(res_pos).state: res_pos = pac_pos pacman.event_manager.trigger_event( DrawDebugLineEvent(self.create_rect_path(res_pos + Vector2(16, 16)), color=Colors.red)) pacman.event_manager.trigger_event( DrawDebugLineEvent(self.create_rect_path(cell_pos + Vector2(16, 16)), color=Colors.pink)) pacman.event_manager.trigger_event( DrawDebugLineEvent([res_pos, cell_pos, pinky_pos], color=Colors.blue)) return res_pos @staticmethod def create_rect_path(pos, size=10): offsets = [ Vector2(size, size), Vector2(size, -size), Vector2(-size, -size), Vector2(-size, size), Vector2(size, size) ] return [pos + off for off in offsets]