diff --git a/assets/stacked_sprites/animated/bla/0.png b/assets/stacked_sprites/animated/bla/0.png new file mode 100644 index 0000000..ae4fe3a Binary files /dev/null and b/assets/stacked_sprites/animated/bla/0.png differ diff --git a/assets/stacked_sprites/animated/bla/1.png b/assets/stacked_sprites/animated/bla/1.png new file mode 100644 index 0000000..c97fe39 Binary files /dev/null and b/assets/stacked_sprites/animated/bla/1.png differ diff --git a/assets/stacked_sprites/animated/bla/2.png b/assets/stacked_sprites/animated/bla/2.png new file mode 100644 index 0000000..632bcb4 Binary files /dev/null and b/assets/stacked_sprites/animated/bla/2.png differ diff --git a/sprite_stacking_engine/cache.py b/sprite_stacking_engine/cache.py index 03cde37..e8366d7 100644 --- a/sprite_stacking_engine/cache.py +++ b/sprite_stacking_engine/cache.py @@ -1,6 +1,6 @@ #!/usr/bin/python3 -import pygame.draw +import os from settings import * @@ -21,6 +21,15 @@ class Cache: layer_array = self.get_layer_array(attrs) self.run_prerender(object_name, layer_array, attrs) + for object_name in ANIMATED_SPRITE_ATTRS: + self.stacked_sprite_cache[object_name] = { + "frames": {} + } + + attrs = ANIMATED_SPRITE_ATTRS[object_name] + frames = self.get_frame_dict(attrs) + self.run_animation_prerender(object_name, frames, attrs) + def run_prerender(self, object_name, layer_array, attrs): outline = attrs.get("outline", True) @@ -29,7 +38,7 @@ class Cache: surface = pygame.transform.rotate(surface, angle * self.viewing_angle) sprite_surface = pygame.Surface([ surface.get_width(), - surface.get_height() + attrs["num_layers"] * attrs["scale"] + surface.get_height() + attrs["layers"] * attrs["scale"] ]) sprite_surface.fill(TRANSP_COLOR) @@ -47,15 +56,51 @@ class Cache: image = pygame.transform.flip(sprite_surface, True, True) self.stacked_sprite_cache[object_name]["rotated_sprites"][angle] = image - def get_layer_array(self, attrs): + def run_animation_prerender(self, object_name, frames, attrs): + outline = attrs.get("outline", True) + + for angle in range(NUM_ANGLES): + surface = pygame.Surface(list(frames.items())[0][1][0].get_size()) + surface = pygame.transform.rotate(surface, angle * self.viewing_angle) + surface_default = pygame.Surface([ + surface.get_width(), + surface.get_height() + attrs["layers"] * attrs["scale"] + ]) + + for frame_name, layer_array in frames.items(): + sprite_surface = surface_default + + sprite_surface.fill(TRANSP_COLOR) + sprite_surface.set_colorkey(TRANSP_COLOR) + + for i, layer in enumerate(layer_array): + layer = pygame.transform.rotate(layer, angle * self.viewing_angle) + sprite_surface.blit(layer, (0, i * attrs["scale"])) + + # get outline + if outline: + outline_coords = pygame.mask.from_surface(sprite_surface).outline() + pygame.draw.polygon(sprite_surface, "black", outline_coords, self.outline_thickness) + + image = pygame.transform.flip(sprite_surface, True, True) + + if not frame_name in self.stacked_sprite_cache[object_name]["frames"]: + self.stacked_sprite_cache[object_name]["frames"][frame_name] = {"rotated_sprites": {}} + + self.stacked_sprite_cache[object_name]["frames"][frame_name]["rotated_sprites"][angle] = image + + def get_layer_array(self, attrs, path=None): + if not path is None: + attrs["path"] = path + # load sprite sheet sprite_sheet = pygame.image.load(attrs["path"]).convert_alpha() # scaling sprite_sheet = pygame.transform.scale(sprite_sheet, vec2(sprite_sheet.get_size()) * attrs["scale"]) sheet_width, sheet_height = sprite_sheet.get_size() - sprite_width = sheet_width // attrs["num_layers"] + sprite_width = sheet_width // attrs["layers"] # new width to prevent error - sheet_width = sprite_width * attrs["num_layers"] + sheet_width = sprite_width * attrs["layers"] # get sprites layer_array = [] @@ -63,4 +108,15 @@ class Cache: sprite = sprite_sheet.subsurface((x, 0, sprite_width, sheet_height)) layer_array.append(sprite) - return layer_array \ No newline at end of file + return layer_array + + def get_frame_dict(self, attrs): + frames = {} + + for path, folders, files in os.walk(attrs["path"]): # loop through all frames and add them to the sheet list + for file in files: + # use the name of the file without the file format specifier (the part before the dot) as the frame name + # and store the layer array + frames[file.split(".")[0]] = self.get_layer_array(attrs, f"{path}/{file}") + + return frames diff --git a/sprite_stacking_engine/scene.py b/sprite_stacking_engine/scene.py index fae4082..1ca6cc3 100644 --- a/sprite_stacking_engine/scene.py +++ b/sprite_stacking_engine/scene.py @@ -4,7 +4,7 @@ from stacked_sprite import * from random import uniform P = "player" -A, B = "Robot", "Building" +A, B, C = "Robot", "Building", "Bla" MAP = [ [0, 0, 0, 0, 0, 0, 0, 0, 0], @@ -13,7 +13,7 @@ MAP = [ [0, 0, A, A, 0, B, B, B, B], [0, 0, A, A, P, B, B, B, B], [0, 0, A, A, 0, B, B, B, B], - [0, 0, 0, 0, 0, B, B, B, B], + [0, 0, 0, 0, C, B, B, B, B], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], ] @@ -39,4 +39,8 @@ class Scene: self.app.player.offset = pos * TILE_SIZE elif name: - StackedSprite(self.app, name=name, pos=rand_pos(pos), rot=rand_rot()) + if name == "Bla": + AnimatedStackedSprite(self.app, name=name, pos=rand_pos(pos), rot=rand_rot()) + + else: + StackedSprite(self.app, name=name, pos=rand_pos(pos), rot=rand_rot()) diff --git a/sprite_stacking_engine/settings.py b/sprite_stacking_engine/settings.py index 011fb09..b3655e7 100644 --- a/sprite_stacking_engine/settings.py +++ b/sprite_stacking_engine/settings.py @@ -18,14 +18,20 @@ TRANSP_COLOR = (43, 64, 36) SPRITE_ATTRS = { "Robot": { "path": "../assets/stacked_sprites/Robot.png", - "num_layers": 16, - "scale": 8, - "y_offset": -8 + "layers": 16, + "scale": 8 }, "Building": { "path": "../assets/stacked_sprites/Building.png", - "num_layers": 80, - "scale": 4, - "y_offset": -40 + "layers": 80, + "scale": 4 + } +} + +ANIMATED_SPRITE_ATTRS = { + "Bla": { + "path": "../assets/stacked_sprites/animated/bla/", + "layers": 3, + "scale": 16 } } \ No newline at end of file diff --git a/sprite_stacking_engine/stacked_sprite.py b/sprite_stacking_engine/stacked_sprite.py index 1726319..6cc3bd3 100644 --- a/sprite_stacking_engine/stacked_sprite.py +++ b/sprite_stacking_engine/stacked_sprite.py @@ -3,7 +3,6 @@ import pygame from settings import * import math -from wobbl_tools import pg class StackedSprite(pygame.sprite.Sprite): @@ -16,7 +15,7 @@ class StackedSprite(pygame.sprite.Sprite): super().__init__(self.group) self.attrs = SPRITE_ATTRS[name] - self.y_offset = vec2(0, -self.attrs["num_layers"] / 2 * self.attrs["scale"]) + self.y_offset = vec2(0, -self.attrs["layers"] / 2 * self.attrs["scale"]) self.cache = self.app.cache.stacked_sprite_cache self.viewing_angle = app.cache.viewing_angle self.rotated_sprites = self.cache[name]["rotated_sprites"] @@ -45,3 +44,52 @@ class StackedSprite(pygame.sprite.Sprite): def get_image(self): self.image = self.rotated_sprites[self.angle] self.rect = self.image.get_rect(center=self.screen_position + self.y_offset) + + +class AnimatedStackedSprite(pygame.sprite.Sprite): + def __init__(self, app, name, pos, rot=0): + self.app = app + self.name = name + self.pos = vec2(pos) * TILE_SIZE + self.player = self.app.player + self.group = self.app.main_group + super().__init__(self.group) + + self.attrs = ANIMATED_SPRITE_ATTRS[name] + self.attrs["animated"] = True + self.y_offset = vec2(0, -self.attrs["layers"] / 2 * self.attrs["scale"]) + self.cache = self.app.cache.stacked_sprite_cache + self.viewing_angle = app.cache.viewing_angle + self.frame = list(self.cache[name]["frames"].keys())[0] + self.rotated_sprites = self.cache[name]["frames"][self.frame]["rotated_sprites"] + self.angle = 0 + self.screen_position = vec2(0) + self.rot = (rot % 360) // self.viewing_angle + + def transform(self): + pos = self.pos - self.player.offset + pos = pos.rotate_rad(self.player.angle) + self.screen_position = pos + CENTER + + def change_layer(self): + self.group.change_layer(self, self.screen_position.y) + + def get_angle(self): + self.angle = -math.degrees(self.player.angle) // self.viewing_angle + self.rot + self.angle = int(self.angle % NUM_ANGLES) + + def animate(self): + self.frame = str(round(self.app.time) * 4 % 3) + print(self.frame) + self.rotated_sprites = self.cache[self.name]["frames"][self.frame]["rotated_sprites"] + + def get_image(self): + self.image = self.rotated_sprites[self.angle] + self.rect = self.image.get_rect(center=self.screen_position + self.y_offset) + + def update(self): + self.transform() + self.get_angle() + self.animate() + self.get_image() + self.change_layer()