From d33b30da75730e6f6890c1378a754b74efbdaf20 Mon Sep 17 00:00:00 2001 From: dimkauzh Date: Fri, 26 Jan 2024 09:05:05 +0100 Subject: [PATCH] Added spritesheets, fixed animation system and added spritesheet support to animation system --- docs/changelog/v5.md | 28 ++++++++- mkdocs.yml | 10 +-- src/fusionengine/__init__.py | 1 + src/fusionengine/engine/animation.py | 31 ++++++---- src/fusionengine/engine/image.py | 35 +++++++++-- src/fusionengine/engine/spritesheets.py | 81 +++++++++++++++++++++++++ tests/spritesheet.py | 17 ++++++ 7 files changed, 178 insertions(+), 25 deletions(-) create mode 100644 src/fusionengine/engine/spritesheets.py create mode 100644 tests/spritesheet.py diff --git a/docs/changelog/v5.md b/docs/changelog/v5.md index 9c8b402..0cab4ca 100644 --- a/docs/changelog/v5.md +++ b/docs/changelog/v5.md @@ -1,6 +1,28 @@ # Version 5 Todo/Changelog ## V5 -- [ ] Docs cleanup -- [ ] New color system -- [ ] Optimised font drawing \ No newline at end of file +- [x] Docs cleanup +- [x] New color system +- [x] Optimised font drawing +- [x] OpenGL rendering + +## V5 +- [ ] New Window features + - [ ] Full Screen + - [ ] is_fullscreen + - [ ] toggle_fullscreen + - [ ] Screen Safer + - [ ] get_screensafer_allowed + - [ ] set_screensafer_allowed + - [ ] get_vsync_enabled + - [ ] get_screen_refresh_rate + - [ ] get_display_amount + - [ ] get_active + +- [ ] SpriteSheet class + - [ ] __init__(Image, width, height) + - [ ] frames (variable with all your extracted frames) + - [ ] draw(speed) (gets a argument speed that specifies the speed of drawing in frames) + +- Image system updates + - [ ] Added crop() function diff --git a/mkdocs.yml b/mkdocs.yml index 9ad1384..b448617 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -30,13 +30,13 @@ nav: - Changelog: - - v5: 'changelog/v5.md' - - v4: 'changelog/v4.md' - - v3: 'changelog/v3.md' + - V5: 'changelog/v5.md' + - V4: 'changelog/v4.md' + - V3: 'changelog/v3.md' - Legacy: - Start: 'legacy/index.md' - - v3: + - V3: - Wiki: - Main page: 'legacy/v3/wiki/wiki.md' - Keys page: 'legacy/v3/wiki/keys.md' @@ -59,7 +59,7 @@ nav: - Setup: 'legacy/v3/tutorials/setup.md' - Basics: 'legacy/v3/tutorials/basics.md' - - v4: + - V4: - Wiki: - Window with fusion: 'legacy/v4/wiki/window.md' - Rendering with fusion: 'legacy/v4/wiki/rendering.md' diff --git a/src/fusionengine/__init__.py b/src/fusionengine/__init__.py index f9419f4..f0b42d6 100644 --- a/src/fusionengine/__init__.py +++ b/src/fusionengine/__init__.py @@ -44,6 +44,7 @@ # Animation from fusionengine.engine.animation import * +from fusionengine.engine.spritesheets import * import pygame as pg diff --git a/src/fusionengine/engine/animation.py b/src/fusionengine/engine/animation.py index 37fb64c..f8d23fb 100644 --- a/src/fusionengine/engine/animation.py +++ b/src/fusionengine/engine/animation.py @@ -1,33 +1,38 @@ from fusionengine.engine.window import Window +from fusionengine.engine.spritesheets import SpriteSheet class Animation: - def __init__(self, window: Window, images: tuple, speed: int) -> None: + def __init__(self, window: Window, images: tuple | SpriteSheet) -> None: """ The class to create a Animation. Args: window: Window - image: tuple of Images + image (Tuple | Spritesheets): Tuple of Images or a SpriteSheet Speed: Int (FPS) """ self.frame = 0 - self.anim = images + if isinstance(images, SpriteSheet): + self.frames = images.frames + elif isinstance(images, tuple): + self.frames = images + else: + ValueError("Images must be a tuple of Images or a SpriteSheet") - self.speed = speed self.window = window - def draw(self) -> None: + def play(self, speed: float) -> None: """ Draw the animation you made before """ - self.window.set_fps(self.speed) + if isinstance(self.frame, int) or ( + isinstance(self.frame, float) and self.frame.is_integer() + ): + if 0 <= int(self.frame) < len(self.frames): + self.frames[int(self.frame)].draw() - if self.frame >= len(self.anim): - self.frame = 0 - - image = self.anim[self.frame] + self.frame += speed - image.draw() - - self.frame += 1 + if self.frame >= len(self.frames): + self.frame = 0 diff --git a/src/fusionengine/engine/image.py b/src/fusionengine/engine/image.py index 3b3d2cc..dc7b662 100644 --- a/src/fusionengine/engine/image.py +++ b/src/fusionengine/engine/image.py @@ -9,17 +9,17 @@ class Image: def __init__( self, - image_path: str, + image_path: str | Imager.Image, x: int, y: int, width: int, height: int, ) -> None: """ - Opens an image. Can be later rendered with draw_image. + Opens an image. Can be later rendered with draw method. Args: - image_path (str): The path to the image + image_path (str or Pillow Image): The path to the image | Pillow Image x (int): X coordinate of the image y (int): Y coordinate of the image width (int): Width of the image (scaling allowed) @@ -31,7 +31,13 @@ def __init__( self.width = width self.height = height - self.image = Imager.open(image_path) + if isinstance(image_path, str): + self.image = Imager.open(str(image_path)) + elif isinstance(image_path, Imager.Image): + self.image = image_path + else: + raise ValueError("Invalid image_path type") + image_data = self.image.tobytes("raw", "RGBA", 0, -1) self.texture = gl.GenTextures(1) @@ -53,6 +59,27 @@ def __init__( gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) + def crop(self, left: int, right: int, top: int, bottom: int) -> "Image": + """ + Crop the image based on the specified boundaries. + + Args: + left (int): The left boundary of the crop area. + right (int): The right boundary of the crop area. + top (int): The top boundary of the crop area. + bottom (int): The bottom boundary of the crop area. + + Returns: + Image: A new Image object representing the cropped image. + """ + return Image( + self.image.crop((left, right, top, bottom)), + self.x, + self.y, + self.width, + self.height, + ) + def draw(self) -> None: """ Draws your image on the screen. diff --git a/src/fusionengine/engine/spritesheets.py b/src/fusionengine/engine/spritesheets.py new file mode 100644 index 0000000..60e5d12 --- /dev/null +++ b/src/fusionengine/engine/spritesheets.py @@ -0,0 +1,81 @@ +from fusionengine.engine.image import Image +from PIL import Image as Imager + + +class SpriteSheet: + def __init__(self, image_path: str, sprite_width: int, sprite_height: int): + """ + Represents a SpriteSheet containing a grid of frames. + + Args: + image_path (str): The path to the sprite sheet image file. + sprite_width (int): Width of each sprite frame. + sprite_height (int): Height of each sprite frame. + """ + self.sprite_sheet = Imager.open(image_path).convert("RGBA") + self.sprite_width = sprite_width + self.sprite_height = sprite_height + self.frames = self.get_frames() + self.width = 0 + self.height = 0 + + def get_frames(self) -> list: + """ + Extract frames from the sprite sheet. + + Returns: + list: List of Image objects representing individual frames. + """ + frames = [] + columns = self.sprite_sheet.width // self.sprite_width + rows = self.sprite_sheet.height // self.sprite_height + + for row in range(rows): + for col in range(columns): + frame = Image(self.extract_frame(col, row), 0, 0, 0, 0) + frames.append(frame) + + return frames + + def extract_frame(self, col: int, row: int) -> Imager.Image: + """ + Extract a single frame from the sprite sheet. + + Args: + col (int): Column index of the sprite. + row (int): Row index of the sprite. + + Returns: + Imager.Image: Image object representing the extracted frame. + """ + x = col * self.sprite_width + y = row * self.sprite_height + + frame = self.sprite_sheet.crop( + (x, y, x + self.sprite_width, y + self.sprite_height) + ) + return frame + + def frame_size(self, width: int, height: int) -> None: + """ + Set the size of each frame in the sprite sheet. + + Args: + width (int): Width to set for each frame. + height (int): Height to set for each frame. + """ + for frame in self.frames: + frame.width = width + frame.height = height + + def frame_pos(self, x: int, y: int) -> None: + """ + Set the position of each frame in the sprite sheet. + + Args: + x (int): X position to set for each frame. + y (int): Y position to set for each frame. + """ + for frame in self.frames: + frame.x = x + frame.y = y diff --git a/tests/spritesheet.py b/tests/spritesheet.py new file mode 100644 index 0000000..f83ee85 --- /dev/null +++ b/tests/spritesheet.py @@ -0,0 +1,17 @@ +import fusionengine as fusion + +window = fusion.Window("Spritesheet test", 500, 500) +window.set_fps(30) +main_image = fusion.Image(fusion.DEBUGIMAGE, 200, 200, 50, 50) + +spr = fusion.SpriteSheet(fusion.DEBUGIMAGE, 100, 100) +spr.frame_size(60, 60) +spr.frame_pos(50, 50) + +anim = fusion.Animation(window, spr) + + +@window.loop +def loop(): + anim.play(0.5) + main_image.draw()