From 08cfd6ed9b171a2d51a71b48c8db80a711d7029c Mon Sep 17 00:00:00 2001 From: Yan Wittmann <2121578@stud.hs-mannheim.de> Date: Thu, 23 Mar 2023 22:24:17 +0100 Subject: [PATCH] Initial files Signed-off-by: Yan Wittmann --- project/PositionScale.py | 14 +++++ project/Sprite.py | 93 ++++++++++++++++++++++++++++++++++ project/Spritesheet.py | 33 ++++++++++++ project/SpritesheetManager.py | 26 ++++++++++ project/main.py | 35 +++++++++++++ project/sprites/sprites.json | 51 +++++++++++++++++++ project/sprites/test_1.png | Bin 0 -> 714 bytes 7 files changed, 252 insertions(+) create mode 100644 project/PositionScale.py create mode 100644 project/Sprite.py create mode 100644 project/Spritesheet.py create mode 100644 project/SpritesheetManager.py create mode 100644 project/main.py create mode 100644 project/sprites/sprites.json create mode 100644 project/sprites/test_1.png diff --git a/project/PositionScale.py b/project/PositionScale.py new file mode 100644 index 0000000..90fde85 --- /dev/null +++ b/project/PositionScale.py @@ -0,0 +1,14 @@ +class PositionScale: + def __init__(self, position=(0, 0), scale=(1, 1)): + self.position = position + self.scale = scale + + def apply_scale_to_position(self): + return self.position[0] * self.scale[0], self.position[1] * self.scale[1] + + @staticmethod + def combine(a, b): + return PositionScale( + (a.position[0] + b.position[0], a.position[1] + b.position[1]), + (a.scale[0] * b.scale[0], a.scale[1] * b.scale[1]) + ) diff --git a/project/Sprite.py b/project/Sprite.py new file mode 100644 index 0000000..9ace5b2 --- /dev/null +++ b/project/Sprite.py @@ -0,0 +1,93 @@ +import pygame + +from PositionScale import PositionScale + + +class Sprite: + def __init__(self, spritesheet): + self.spritesheet = spritesheet + + self.animation_state = list(self.spritesheet.animations.keys())[0] + self.animation_frame = 0 + self.animation_delay = 0 + + self.position_scale = PositionScale() + + self.visible = True + self.animated = True + + self.image = None + + def tick(self, dt): + animation = self.spritesheet.animations[self.animation_state] + + if self.animated: + self.animation_delay += dt + + while self.animation_delay >= animation["delays"][self.animation_frame % len(animation["delays"])]: + self.animation_delay -= animation["delays"][self.animation_frame % len(animation["delays"])] + self.animation_frame = (self.animation_frame + 1) % len(animation["images"]) + + self.image = animation["images"][self.animation_frame % len(animation["images"])] + + def set_animation_state(self, state): + if state in self.spritesheet.animations: + self.animation_state = state + self.animation_delay = 0 + self.tick(0) + + def set_animation_frame(self, frame): + self.animation_frame = frame + self.tick(0) + + def draw(self, screen, screen_transform): + if not self.visible: + return + + if self.image is not None: + target_position = screen_transform.apply_scale_to_position() + + target_scale = ( + screen_transform.scale[0] * self.position_scale.scale[0], + screen_transform.scale[1] * self.position_scale.scale[1] + ) + target_size = ( + int(target_scale[0] * self.image.get_width()), + int(target_scale[1] * self.image.get_height()) + ) + + target_image = self.get_scaled_image(self.image, target_size) + + screen.blit(target_image, target_position) + + def get_scaled_image(self, image, resize): + return pygame.transform.scale(image, resize) + + def dump(self, file): + # re-attach all the images to a single surface and save it to a file + width = 0 + height = 0 + + for animation in self.spritesheet.animations.values(): + max_height = 0 + total_width = 0 + for image in animation["images"]: + total_width += image.get_width() + max_height = max(max_height, image.get_height()) + width = max(width, total_width) + height += max_height + + sheet = pygame.Surface((width, height)) + + x = 0 + y = 0 + for animation in self.spritesheet.animations.values(): + max_height = 0 + for image in animation["images"]: + sheet.blit(image, (x, y)) + x += image.get_width() + max_height = max(max_height, image.get_height()) + y += max_height + x = 0 + + pygame.image.save(sheet, file) diff --git a/project/Spritesheet.py b/project/Spritesheet.py new file mode 100644 index 0000000..cedd8f2 --- /dev/null +++ b/project/Spritesheet.py @@ -0,0 +1,33 @@ +import pygame + + +class Spritesheet: + def __init__(self, sprites_dir, sprite_data): + self.sprite_id = sprite_data["id"] + self.subsheets = sprite_data["subsheets"] + + self.sheet_file = sprites_dir + "/" + self.sprite_id + ".png" + self.sheet = pygame.image.load(self.sheet_file).convert_alpha() + + self.animations = {} + self.load_animations() + + def load_animations(self): + subsheet_y = 0 + + for subsheet_data in self.subsheets: + subsheet_id = subsheet_data["id"] + delays = subsheet_data["delays"] + subsheet_width = subsheet_data["width"] + subsheet_height = subsheet_data["height"] + subsheet_x = 0 + + subsheet_images = [] + for i in range(len(delays)): + subsheet_image = pygame.Surface((subsheet_width, subsheet_height), pygame.SRCALPHA) + subsheet_image.blit(self.sheet, (0, 0), (subsheet_x, subsheet_y, subsheet_width, subsheet_height)) + subsheet_images.append(subsheet_image) + subsheet_x += subsheet_width + + subsheet_y += subsheet_height + self.animations[subsheet_id] = {"images": subsheet_images, "delays": delays} diff --git a/project/SpritesheetManager.py b/project/SpritesheetManager.py new file mode 100644 index 0000000..50abcb4 --- /dev/null +++ b/project/SpritesheetManager.py @@ -0,0 +1,26 @@ +import pygame +import json + +from Spritesheet import Spritesheet + + +# This class is used to load named sprite sheets from the img folder. +class SpritesheetManager: + def __init__(self, sprites_dir, config_file): + self.sprites_dir = sprites_dir + self.spritesheets = {} + self.load_from_config(config_file) + + def load_from_config(self, config_file): + print("Loading sprites from sprite sheet config file", config_file) + config = json.load(open(config_file)) + + for sprite_data in config: + sprite_sheet = Spritesheet(self.sprites_dir, sprite_data) + self.spritesheets[sprite_sheet.sprite_id] = sprite_sheet + print("Loaded", len(self.spritesheets), "sprite sheet(s)") + + def get_sheet(self, sheet): + if sheet not in self.spritesheets: + raise ValueError("Invalid/Missing sprite sheet " + sheet) + return self.spritesheets[sheet] diff --git a/project/main.py b/project/main.py new file mode 100644 index 0000000..c7d06a2 --- /dev/null +++ b/project/main.py @@ -0,0 +1,35 @@ +import random + +import pygame + +from PositionScale import PositionScale +from SpritesheetManager import SpritesheetManager +from Sprite import Sprite + +pygame.init() +screen = pygame.display.set_mode((300, 300)) +pygame.display.set_caption("PE GAME") +clock = pygame.time.Clock() + +spritesheet_manager = SpritesheetManager("sprites", "sprites/sprites.json") + +test_1_sprite = Sprite(spritesheet_manager.get_sheet("test_1")) + +test_1_sprite.dump("debug.png") + +while True: + clock.tick(5) + + for event in pygame.event.get(): + if event.type == pygame.QUIT: + pygame.quit() + + screen.fill((0, 0, 0)) + + test_1_sprite.tick(1) + test_1_sprite.draw(screen, PositionScale((40, 40), (3, 3))) + pygame.display.update() + + if random.randint(1, 10) == 1: + test_1_sprite.set_animation_state(random.choice(["walk_r", "walk_l", "idle", "other_test"])) + print(test_1_sprite.animation_state) diff --git a/project/sprites/sprites.json b/project/sprites/sprites.json new file mode 100644 index 0000000..f7e3e93 --- /dev/null +++ b/project/sprites/sprites.json @@ -0,0 +1,51 @@ +[ + { + "id": "test_1", + "subsheets": [ + { + "id": "walk_l", + "delays": [ + 1, + 2, + 3, + 1 + ], + "height": 16, + "width": 16 + }, + { + "id": "idle", + "delays": [ + 1, + 1, + 3, + 1, + 1 + ], + "height": 16, + "width": 16 + }, + { + "id": "walk_r", + "delays": [ + 1, + 1, + 1, + 1 + ], + "height": 16, + "width": 16 + }, + { + "id": "other_test", + "delays": [ + 1, + 1, + 1 + ], + "height": 16, + "width": 16 + } + ] + } +] \ No newline at end of file diff --git a/project/sprites/test_1.png b/project/sprites/test_1.png new file mode 100644 index 0000000000000000000000000000000000000000..c6e43f4a3dcf449007b62b24391c89690d673acb GIT binary patch literal 714 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r53?z4+XPOVB7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h2Ka=y>h?@!c=aF1`+8%}Rv^V(666>B|3Aa^Q@lX644efXk;M!Qe1}1p z@p%4<6b1$+Yfl%)kO=puQyTk}6?vSu|NY;;?*`|QGXm>6yvi4U?P?J};BfTK`IYy? zW3_L~?0g;AwtjkRCr3vcCK<)_MvCk5Ot{iec;mpKD+cQxSWoL&@p+=Q^F=fF zcU4Uf7am%!ed>OQMffzHx@~3D3*_xh?5NDsazuKtYVrV!=rQOc;JJ# zsmC>yTN^i*vh@{euQ>OUvF-otM=7z#!+Y+0RXAL+`jE~P8E!A<|0PrQhUOe$Ep~o) zt}xMXM&X=MEPG&kx)3&a@W%z~lJKP{S8neEy1JddN%aDf)D%w>gHIR{Kk zzr6L~zINZ>7dI9!eV4km`Mmz(jjgQ3j+=O+9eMu#pJn)#CL{y}c32fuiH zlQePJZkp<+on8GdFXz2y@xH$J~dtmAe%CwvNJ9 zd7E_kWgcsEocsB^?SE9^q@dg3T`{i$+iIejx0gRF;?tPQ{b#NA;=4B%iisEG$1n0} z)~;SICidgry(xmN_WL)TeIj^aN}z;c~$&1gD sFM7o&{L*&U`^s|I_vkLC_uu8$+Ne%g{mH}un6?-^UHx3vIVCg!07q#%RR910 literal 0 HcmV?d00001