diff --git a/sprite_stacking_engine/cache.py b/sprite_stacking_engine/cache.py index 9cb2568..3bc0b5c 100644 --- a/sprite_stacking_engine/cache.py +++ b/sprite_stacking_engine/cache.py @@ -13,17 +13,17 @@ class Cache: self.get_stacked_sprite_cache() def get_stacked_sprite_cache(self): - for object_name in SPRITE_ATTRS: - self.stacked_sprite_cache[object_name] = { + for object_name in SPRITE_ATTRS: # loop through static sprites and create a dict with a dict inside for the - + self.stacked_sprite_cache[object_name] = { # stacked sprites of each rotation "rotated_sprites": {} } attrs = SPRITE_ATTRS[object_name] - layer_array = self.get_layer_array(attrs) - self.run_prerender(object_name, layer_array, attrs) + layer_array = self.get_layer_array(attrs) # get a list of all sprites + self.run_prerender(object_name, layer_array, attrs) # render the sprites into one 3d looking sprite - for object_name in ANIMATED_SPRITE_ATTRS: - self.stacked_sprite_cache[object_name] = { + for object_name in ANIMATED_SPRITE_ATTRS: # loop through all animated sprites and do the same as in the first - + self.stacked_sprite_cache[object_name] = { # loop but for every frame "frames": {} } @@ -31,50 +31,53 @@ class Cache: 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", OUTLINE) + def run_prerender(self, object_name, layer_array, attrs): # prerender static sprite + outline = attrs.get("outline", OUTLINE) # load the outline attribute of the sprite or use the default if the - + # attribute does not exist - for angle in range(NUM_ANGLES): - surface = pygame.Surface(layer_array[0].get_size()) - surface = pygame.transform.rotate(surface, angle * self.viewing_angle) + for angle in range(NUM_ANGLES): # prerender each angle + surface = pygame.Surface(layer_array[0].get_size()) # this code lump calculates the size of the sprite - + surface = pygame.transform.rotate(surface, angle * self.viewing_angle) # surface and creates it sprite_surface = pygame.Surface([ surface.get_width(), surface.get_height() + attrs["layers"] * attrs["scale"] ]) - sprite_surface.fill(TRANSP_COLOR) + sprite_surface.fill(TRANSP_COLOR) # make just one color transparent to save the alpha channel and RAM usage sprite_surface.set_colorkey(TRANSP_COLOR) - for i, layer in enumerate(layer_array): + for i, layer in enumerate(layer_array): # blit each layer onto the sprite surface 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) + outline_coords = pygame.mask.from_surface(sprite_surface).outline() # get the coordinates of the - + # outline + pygame.draw.polygon(sprite_surface, "black", outline_coords, self.outline_thickness) # draw the - + # outline - image = pygame.transform.flip(sprite_surface, True, True) - self.stacked_sprite_cache[object_name]["rotated_sprites"][angle] = image + image = pygame.transform.flip(sprite_surface, True, True) # mirror the image + self.stacked_sprite_cache[object_name]["rotated_sprites"][angle] = image # add the surface to the cache - def run_animation_prerender(self, object_name, frames, attrs): - outline = attrs.get("outline", OUTLINE) + def run_animation_prerender(self, object_name, frames, attrs): # does the same as the run_prerender function but - + outline = attrs.get("outline", OUTLINE) # for every frame of the animated sprite 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 = pygame.Surface(list(frames.items())[0][1][0].get_size()) # empty sprite surface gets generated - + surface = pygame.transform.rotate(surface, angle * self.viewing_angle) # here as a default to improve - + surface_default = pygame.Surface([ # start time surface.get_width(), surface.get_height() + attrs["layers"] * attrs["scale"] ]) - for frame_name, layer_array in frames.items(): + for frame_name, layer_array in frames.items(): # render all frames sprite_surface = surface_default sprite_surface.fill(TRANSP_COLOR) sprite_surface.set_colorkey(TRANSP_COLOR) - for i, layer in enumerate(layer_array): + for i, layer in enumerate(layer_array): # blit each layer to the sprite surface layer = pygame.transform.rotate(layer, angle * self.viewing_angle) sprite_surface.blit(layer, (0, i * attrs["scale"])) @@ -85,14 +88,16 @@ class Cache: image = pygame.transform.flip(sprite_surface, True, True) - if not frame_name in self.stacked_sprite_cache[object_name]["frames"]: + if not frame_name in self.stacked_sprite_cache[object_name]["frames"]: # add the frame's subdict with - + # the rotated sprites cache self.stacked_sprite_cache[object_name]["frames"][frame_name] = {"rotated_sprites": {}} + # save the frame's sprite 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 + if not path is None: # use path parameter if it is an animated sprite because animated sprites have a path to - + attrs["path"] = path # a folder instead of a path to just one image file as attribute # load sprite sheet sprite_sheet = pygame.image.load(attrs["path"]).convert_alpha() @@ -116,8 +121,8 @@ class Cache: 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 + # 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/player.py b/sprite_stacking_engine/player.py index d49deee..870d3ad 100644 --- a/sprite_stacking_engine/player.py +++ b/sprite_stacking_engine/player.py @@ -10,9 +10,10 @@ class Player(pygame.sprite.Sprite): self.group = app.main_group super().__init__(self.group) - self.group.change_layer(self, CENTER.y) + self.group.change_layer(self, CENTER.y) # simpy set the layer to the y center of the screen because that is - + # the position of the player on the screen - size = vec2([50, 50]) + size = vec2([50, 50]) # create a surface with a red dot on it self.image = pygame.Surface(size, pygame.SRCALPHA) pygame.draw.circle(self.image, "red", size / 2, size[0] / 2) self.rect = self.image.get_rect(center=CENTER) @@ -22,7 +23,7 @@ class Player(pygame.sprite.Sprite): self.angle = 0 self.diag_move_corr = 1 / math.sqrt(2) - def control(self): + def control(self): # move the player and rotate the view according to the key presses self.inc = vec2(0) speed = PLAYER_SPEED * self.app.delta_time rot_speed = PLAYER_ROT_SPEED * self.app.delta_time diff --git a/sprite_stacking_engine/scene.py b/sprite_stacking_engine/scene.py index 1ca6cc3..18684a2 100644 --- a/sprite_stacking_engine/scene.py +++ b/sprite_stacking_engine/scene.py @@ -6,7 +6,7 @@ from random import uniform P = "player" A, B, C = "Robot", "Building", "Bla" -MAP = [ +MAP = [ # two dimensional object matrix [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, B, B, B, B], @@ -31,7 +31,7 @@ class Scene: rand_rot = lambda: uniform(0, 360) rand_pos = lambda pos: pos + vec2(uniform(-0.25, 0.25)) - for j, row in enumerate(MAP): + for j, row in enumerate(MAP): # loop through all objects on the map for i, name in enumerate(row): pos = vec2(i, j) + vec2(0.5) diff --git a/sprite_stacking_engine/stacked_sprite.py b/sprite_stacking_engine/stacked_sprite.py index 135a8d7..5d74b74 100644 --- a/sprite_stacking_engine/stacked_sprite.py +++ b/sprite_stacking_engine/stacked_sprite.py @@ -14,8 +14,9 @@ class StackedSprite(pygame.sprite.Sprite): self.group = self.app.main_group super().__init__(self.group) - self.attrs = SPRITE_ATTRS[name] - self.y_offset = vec2(0, -self.attrs["layers"] / 2 * self.attrs["scale"]) + self.attrs = SPRITE_ATTRS[name] # get attributes + self.y_offset = vec2(0, -self.attrs["layers"] / 2 * self.attrs["scale"]) # calculate the y offset because the - + # images position in pygame is the position of the top left corner of the image and we use the bottom position self.cache = self.app.cache.stacked_sprite_cache self.viewing_angle = app.cache.viewing_angle self.rotated_sprites = self.cache[name]["rotated_sprites"] @@ -23,15 +24,15 @@ class StackedSprite(pygame.sprite.Sprite): self.screen_position = vec2(0) self.rot = (rot % 360) // self.viewing_angle - def transform(self): + def transform(self): # recalculate position and rotation pos = self.pos - self.player.offset pos = pos.rotate_rad(self.player.angle) self.screen_position = pos + CENTER - def change_layer(self): + def change_layer(self): # calculate the layer to draw over the things behind it and not the other way self.group.change_layer(self, self.screen_position.y) - def get_angle(self): + def get_angle(self): # calculate the angle to draw the right sprite self.angle = -math.degrees(self.player.angle) // self.viewing_angle + self.rot self.angle = int(self.angle % NUM_ANGLES) @@ -46,7 +47,7 @@ class StackedSprite(pygame.sprite.Sprite): self.rect = self.image.get_rect(center=self.screen_position + self.y_offset) -class AnimatedStackedSprite(pygame.sprite.Sprite): +class AnimatedStackedSprite(pygame.sprite.Sprite): # stacked sprite class for animated sprites, works nearly the same def __init__(self, app, name, pos, rot=0): self.app = app self.name = name @@ -56,11 +57,11 @@ class AnimatedStackedSprite(pygame.sprite.Sprite): 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.attrs["animated"] = True # set the animated attribute to true so that the cache knows that this sprite - + self.y_offset = vec2(0, -self.attrs["layers"] / 2 * self.attrs["scale"]) # is animated 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.frame = list(self.cache[name]["frames"].keys())[0] # set the frame to the first frame self.rotated_sprites = self.cache[name]["frames"][self.frame]["rotated_sprites"] self.angle = 0 self.screen_position = vec2(0) @@ -78,7 +79,7 @@ class AnimatedStackedSprite(pygame.sprite.Sprite): self.angle = -math.degrees(self.player.angle) // self.viewing_angle + self.rot self.angle = int(self.angle % NUM_ANGLES) - def animate(self): + def animate(self): # change the layer to display another frame self.frame = str(round(self.app.time) * 4 % 3) self.rotated_sprites = self.cache[self.name]["frames"][self.frame]["rotated_sprites"]