From c33ecbeb013d5e9a89d7cc9c5d1d53ee47af89fa Mon Sep 17 00:00:00 2001 From: kerodekroma Date: Sun, 11 Feb 2024 21:46:58 -0500 Subject: [PATCH] first release --- .editorconfig | 8 + .github/workflows/build_pygame.yml | 42 +++ .github/workflows/publish_itchio.yml | 25 ++ .gitignore | 5 + assets/font/PixelSimpel.otf | Bin 0 -> 23320 bytes .../python-pygame-loader.json | 298 ++++++++++++++++++ .../python-pygame-loader.png | Bin 0 -> 5397 bytes atlas_spritesheet.py | 101 ++++++ main.py | 52 +++ 9 files changed, 531 insertions(+) create mode 100644 .editorconfig create mode 100644 .github/workflows/build_pygame.yml create mode 100644 .github/workflows/publish_itchio.yml create mode 100644 .gitignore create mode 100644 assets/font/PixelSimpel.otf create mode 100644 assets/python-pygame-loader/python-pygame-loader.json create mode 100644 assets/python-pygame-loader/python-pygame-loader.png create mode 100644 atlas_spritesheet.py create mode 100644 main.py diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..1eaff1a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +# EditorConfig is awesome: https://EditorConfig.org +# top-most EditorConfig file +root = true + +# 4 space indentation +[*.py] +indent_style = space +indent_size = 4 diff --git a/.github/workflows/build_pygame.yml b/.github/workflows/build_pygame.yml new file mode 100644 index 0000000..748887a --- /dev/null +++ b/.github/workflows/build_pygame.yml @@ -0,0 +1,42 @@ +name: Build this project using Pygbag +on: + push: + branches: + - main + +jobs: + build: + name: building project + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: checkout + uses: actions/checkout@v3.3.0 + with: + persist-credentials: false + fetch-depth: 0 + + - name: build + run: | + echo "Installing pygbag" + python3 -m pip install pygbag + echo "pygbag success!" + echo "building the project" + python3 -m pygbag --build $GITHUB_WORKSPACE/main.py + echo "DONE" + zip -j -r python_pygame_loader_demo.zip build/web + + - name: Generate release tag + id: generate_release_tag + uses: alexvingg/next-release-tag@v1.0.4 + with: + github_token: ${{ secrets.GH_CREDENTIALS }} + tag_prefix: "" + + - name: Create Release + uses: ncipollo/release-action@v1 + with: + token: ${{ secrets.GH_CREDENTIALS }} + tag: ${{ steps.generate_release_tag.outputs.release_tag }} + artifacts: python_pygame_loader_demo.zip diff --git a/.github/workflows/publish_itchio.yml b/.github/workflows/publish_itchio.yml new file mode 100644 index 0000000..38d9895 --- /dev/null +++ b/.github/workflows/publish_itchio.yml @@ -0,0 +1,25 @@ +name: "Publish to itch.io" +on: + release: + types: [published] +jobs: + release_html: + runs-on: ubuntu-latest + name: fecth from GITHUB + steps: + - name: download zip file + id: download-html + uses: dsaltares/fetch-gh-release-asset@master + with: + file: python_pygame_loader_demo.zip + env: + GITHUB_TOKEN: "${{ secrets.GH_CREDENTIALS }}" + - name: deploy to itch.io (HTML5) + uses: josephbmanley/butler-publish-itchio-action@master + env: + BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }} + CHANNEL: HTML + ITCH_GAME: python-pygame-loader + ITCH_USER: kerodekroma + PACKAGE: python_pygame_loader_demo.zip + VERSION: ${{ steps.download-html.outputs.version }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a6cfebf --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +__pycache__ +.DS_Store +build + +.gacm \ No newline at end of file diff --git a/assets/font/PixelSimpel.otf b/assets/font/PixelSimpel.otf new file mode 100644 index 0000000000000000000000000000000000000000..010e30f1cd23d5923ccba93385512ea2c3d7c159 GIT binary patch literal 23320 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 T4p)>q=}XCqCAG=iO#uG~c0CS2 literal 0 HcmV?d00001 diff --git a/assets/python-pygame-loader/python-pygame-loader.json b/assets/python-pygame-loader/python-pygame-loader.json new file mode 100644 index 0000000..a8d74b0 --- /dev/null +++ b/assets/python-pygame-loader/python-pygame-loader.json @@ -0,0 +1,298 @@ +{ "frames": { + "python-pygame-loader 0.ase": { + "frame": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 1.ase": { + "frame": { "x": 48, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 2.ase": { + "frame": { "x": 96, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 3.ase": { + "frame": { "x": 144, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 4.ase": { + "frame": { "x": 192, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 5.ase": { + "frame": { "x": 240, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 6.ase": { + "frame": { "x": 288, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 7.ase": { + "frame": { "x": 336, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 8.ase": { + "frame": { "x": 384, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 9.ase": { + "frame": { "x": 432, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 10.ase": { + "frame": { "x": 480, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 11.ase": { + "frame": { "x": 528, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 12.ase": { + "frame": { "x": 576, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 13.ase": { + "frame": { "x": 624, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 14.ase": { + "frame": { "x": 672, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 15.ase": { + "frame": { "x": 720, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 16.ase": { + "frame": { "x": 768, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 17.ase": { + "frame": { "x": 816, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 18.ase": { + "frame": { "x": 864, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 19.ase": { + "frame": { "x": 912, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 20.ase": { + "frame": { "x": 960, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 21.ase": { + "frame": { "x": 1008, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 22.ase": { + "frame": { "x": 1056, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 23.ase": { + "frame": { "x": 1104, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 24.ase": { + "frame": { "x": 1152, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 25.ase": { + "frame": { "x": 1200, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 26.ase": { + "frame": { "x": 1248, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 27.ase": { + "frame": { "x": 1296, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 28.ase": { + "frame": { "x": 1344, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 29.ase": { + "frame": { "x": 1392, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 30.ase": { + "frame": { "x": 1440, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 31.ase": { + "frame": { "x": 1488, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 32.ase": { + "frame": { "x": 1536, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 33.ase": { + "frame": { "x": 1584, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + }, + "python-pygame-loader 34.ase": { + "frame": { "x": 1632, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 60 + } + }, + "meta": { + "app": "https://www.aseprite.org/", + "version": "1.3.2-arm64", + "image": "python-pygame-loader.png", + "format": "I8", + "size": { "w": 1680, "h": 48 }, + "scale": "1", + "frameTags": [ + ], + "layers": [ + { "name": "animation", "opacity": 255, "blendMode": "normal" } + ], + "slices": [ + ] + } +} diff --git a/assets/python-pygame-loader/python-pygame-loader.png b/assets/python-pygame-loader/python-pygame-loader.png new file mode 100644 index 0000000000000000000000000000000000000000..1a5fc54486d93a2cb6ddd28802df45c9f86428cc GIT binary patch literal 5397 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 literal 0 HcmV?d00001 diff --git a/atlas_spritesheet.py b/atlas_spritesheet.py new file mode 100644 index 0000000..92d33a2 --- /dev/null +++ b/atlas_spritesheet.py @@ -0,0 +1,101 @@ +import pygame +import json +import time + +class AtlasSpritesheet: + """ + A single class used to render a png spritesheet file which has a json file as a metadata + + """ + def __init__(self, image_path, json_path, scale=1): + """ + Parameters: + ----------- + image_path: str + The path of the file in png format + json_path: str + The path of the file in json format which contains the metadata + scale: int, optional + How much to do you want to scale the sprite, by default is 1 + """ + self.spritesheet = pygame.image.load(image_path).convert_alpha() + self.sprite_datalist = [] + self.current_frame = 0 + self.start_time = time.time() + self.scale = scale + with open(json_path) as file: + self.data = json.load(file) + file.close() + self.sprite_datalist = list(self.data['frames']) + + def get_width(self): + """ + Returns: + ----------- + width + the width of the sprite from the json file, using the first frame as a reference + """ + return self.data['frames'][self.sprite_datalist[0]]['frame']['w'] * self.scale + + def get_height(self): + """ + Returns: + ----------- + height + the height of the sprite from the json file, using the first frame as a reference + """ + return self.data['frames'][self.sprite_datalist[0]]['frame']['h'] * self.scale + + def setup(self, position=(0,0), frame_rate=0, repeat=False): + """ + Parameters: + ----------- + position: tuple, optional + this param is to define the position of the sprite in the screen, by default (0,0) + + frame_rate: int, optional + this param is to set a custom animation speed, by default is 0, that means that it will take the default speed of the json file data + + repeat: bool, optional + this param defines if the animation must be shown as a loop or not + """ + self.position = position + self.frame_rate = frame_rate + self.repeat = repeat + + def get_sprite(self, x, y, width, height): + sprite = pygame.Surface((width * self.scale, height * self.scale)) + sprite.set_colorkey((0,0,0)) + spritesheet_scaled = pygame.transform.scale(self.spritesheet, (self.spritesheet.get_width() * self.scale, self.spritesheet.get_height() * self.scale)) + sprite.blit(spritesheet_scaled, (0, 0), (x * self.scale, y * self.scale, width * self.scale, height * self.scale)) + return sprite + + def get_frame_rate(self, frame=0): + if self.frame_rate > 0: + return self.frame_rate + return self.data['frames'][self.sprite_datalist[frame]]['duration'] + + def parse_sprite(self, frame = 0): + sprite = self.data['frames'][self.sprite_datalist[frame]]['frame'] + x, y, w, h = sprite['x'], sprite['y'], sprite['w'], sprite['h'] + image = self.get_sprite(x, y, w, h) + return image + + def play(self, screen): + """ + Parameters: + ----------- + screen: Surface + this param is to indicate in which surface the sprite must be shown + """ + frame_rate = self.get_frame_rate(self.current_frame) + diff_time = (time.time() - self.start_time ) + if diff_time > float(1/frame_rate): + self.start_time = time.time() + if self.current_frame < len(self.sprite_datalist): + self.current_frame += 1 + if self.repeat == False and self.current_frame == len(self.sprite_datalist): + self.current_frame = len(self.sprite_datalist) - 1 + if self.repeat == True and self.current_frame == len(self.sprite_datalist): + self.current_frame = 0 + screen.blit(self.parse_sprite(self.current_frame), self.position) \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..f273ecb --- /dev/null +++ b/main.py @@ -0,0 +1,52 @@ +import asyncio +import pygame +import atlas_spritesheet +import sys + +WIDTH, HEIGHT = 480, 480 + +class App: + def __init__(self): + pygame.init() + self.screen = pygame.display.set_mode((WIDTH, HEIGHT)) + pygame.display.set_caption("Demo of PythonPygameLoader") + #bg gradient + self.surface_bg = self.bg_gradient((WIDTH, HEIGHT), (85, 37, 125), (40, 18, 60), HEIGHT) + #font + self.font = pygame.font.Font('assets/font/PixelSimpel.otf', 32) + # atlas setup + file_path = 'assets/python-pygame-loader/python-pygame-loader' + self.loader_spritesheet = atlas_spritesheet.AtlasSpritesheet(f'{file_path}.png', f'{file_path}.json', 4) + center_pos = (WIDTH/2 - self.loader_spritesheet.get_width()/2, HEIGHT/2 - self.loader_spritesheet.get_height()/2) + self.loader_spritesheet.setup(center_pos, 20, True) + + def bg_gradient(self, size, color, lerp_color, length): + surface = pygame.Surface((1, length)) + _color = pygame.Color(color) + _lerp_color = pygame.Color(lerp_color) + for i in range(length): + c = _color.lerp(_lerp_color, i/length) + surface.set_at((0, i), c) + return pygame.transform.scale(surface, size) + + async def render(self): + while True: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + pygame.quit() + sys.exit() + break + + self.screen.fill((85, 37, 125)) + # gradient bg + self.screen.blit(self.surface_bg, (0, 0)) + # play loader + self.loader_spritesheet.play(self.screen) + + #update the display + pygame.display.flip() + # clock + pygame.time.Clock().tick(60) + await asyncio.sleep(0) + +asyncio.run(App().render()) \ No newline at end of file