Compare commits

...

11 Commits

Author SHA1 Message Date
08d6261d3f Updated version 2025-06-21 23:08:39 +01:00
95afab5431 Implemented centre() 2025-06-21 23:03:38 +01:00
d897a3744d Added documentation for createAnimObj() 2025-06-21 23:03:27 +01:00
4161ec9c5b Added timeouts 2025-06-21 23:03:11 +01:00
a421069a63 Added animation events 2025-06-20 19:26:43 +01:00
44f75b52d0 Added size property 2025-06-20 18:21:50 +01:00
8f2dcbfd21 Removed unused debug functions 2025-06-20 18:08:13 +01:00
2c79adaf36 So apparently if you don't put all of this in it's own directory, pip explodes. 2025-06-20 18:05:57 +01:00
f4e0e24774 Moved initialisation from BaseGame to GameUtils 2025-06-20 14:46:29 +01:00
f1c6c7c23d Implemented AnimatedObject 2025-06-20 12:27:48 +01:00
8ce99ca561 Fixed typo 2025-06-20 12:27:29 +01:00
10 changed files with 213 additions and 98 deletions

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "gameUtils"
version = "0.0.1.dev0"
version = "1.0.0"
description = "A set of utilities to make the game development process less painful (despite the organisation name) and more unified."
authors = [{ name = "Brosef" }]
dependencies = ["pygame-ce"]

View File

@ -1,4 +0,0 @@
from .base import Game
from .utils import centre
from .anim import AnimatedHandler
from .anim import AnimatedObject

View File

@ -1,67 +0,0 @@
import pygame
class AnimationHandler(pygame.Surface):
"""
TODO: Write documentation on this, it takes in a
webp file, and handles drawing it to the screen in
a way that doesn't rely on frame counters.
"""
def __init__(self, animationPath):
pass
class AnimatedObject:
"""
"""
def __init__(self, baseFrame):
"""
Args:
baseFrame: A still image that gets displayed when no other animation is playing.
"""
self._currentAnim = None
self._animStart = None
self._animations = {}
#self.baseFrame = pygame.image.load(baseFrame)
def getFrame(self) -> pygame.Surface:
"""
Gets the current frame based on which animation is playing, and the current time.
Returns:
pygame.Surface: The current frame
"""
def addAnimation(self, animation: AnimationHandler, animationID: str):
"""
Adds an animation to the object.
Args:
animation (AnimationHandler): The actual animation.
animationID (str): The ID that's later used to play the animation.
"""
self.animations.update({animationID: animation})
def playAnim(self, animationID: str):
"""
Plays an animation.
Args:
animationID (str): The animation ID to play.
"""
self._animStart = time.perf_counter()
self._currentAnim = self._animations[animationID]
def get_view(self):
print(f'get_view called')
return super().get_view()
def get_buffer(self):
print(f'get_buffer called')
return super().get_buffer()
def copy(self):
print(f'copy called')
return super().copy()

View File

@ -1,12 +0,0 @@
class Game:
def __init__(self):
pass
def update(self):
pass
def onEvent(self, event):
pass
def close(self):
pass

View File

@ -0,0 +1,7 @@
from .base import Game
from .utils import centre
from .anim import AnimationHandler
from .anim import AnimatedObject
from .events import AnimStart
from .events import AnimFinish
from .events import Timeout

85
src/gameUtils/anim.py Normal file
View File

@ -0,0 +1,85 @@
from .events import AnimStart
from .events import AnimFinish
import pygame
import time
class AnimationHandler(pygame.Surface):
"""
TODO: Write documentation on this, it takes in a
webp file, and handles drawing it to the screen in
a way that doesn't rely on frame counters.
"""
def __init__(self, animationID: str, animationPath: str, fps: int):
self.animationID = animationID
self.frames = pygame.image.load_animation(animationPath)
self.fps = fps
self.frameCount = len(self.frames)
# TODO: Add optional transparancy?
class AnimatedObject:
"""
"""
def __init__(self, _game, objectID: str, baseFrame: str):
"""
Args:
_game: Used internally.
objectID: Used to identify this animated object.
baseFrame: A still image that gets displayed when no other animation is playing.
"""
self._game = _game
self.objectID = objectID
self._currentAnim = None
self._animStart = None
self._animations = {}
self.baseFrame = pygame.image.load(baseFrame)
self.size = self.baseFrame.size
self._surf = pygame.Surface(self.baseFrame.size)
def getFrame(self) -> pygame.Surface:
"""
Gets the current frame based on which animation is playing, and the current time.
Returns:
pygame.Surface: The current frame
"""
self._surf.fill((0, 0, 0))
if self._currentAnim == None:
self._surf.blit(self.baseFrame, (0, 0))
else:
frame = min(round((time.perf_counter() - self._animStart) * self._currentAnim.fps), self._currentAnim.frameCount - 1)
self._surf.blit(self._currentAnim.frames[frame][0], (0, 0))
if frame == self._currentAnim.frameCount - 1:
self._game.onEvent(AnimFinish(self.objectID, self._currentAnim.animationID))
self._currentAnim = None
self._animStart = None
return self._surf
def addAnimation(self, animation: AnimationHandler):
"""
Adds an animation to the object.
Args:
animation (AnimationHandler): The actual animation.
animationID (str): The ID that's later used to play the animation.
"""
self._animations.update({animation.animationID: animation})
def playAnim(self, animationID: str, overrideCurrentAnim=False):
"""
Plays an animation.
Args:
animationID (str): The animation ID to play.
"""
if self._currentAnim != None and not overrideCurrentAnim: return
self._animStart = time.perf_counter()
self._currentAnim = self._animations[animationID]
self._game.onEvent(AnimStart(self.objectID, animationID))

62
src/gameUtils/base.py Normal file
View File

@ -0,0 +1,62 @@
from .events import Timeout
from .anim import AnimatedObject
import tomllib
import time
class Game:
def __init__(self, surface):
"""
Initialises some things for the game developers.
Args:
surface (pygame.Surface): The surface the game devs draw on.
"""
self.surf = surface
self.size = self.surf.size
self.pm = None
# Holds all the timeouts that haven't been fired yet
self._timeouts = []
with open('./game.toml', 'r') as f:
self.cfg = tomllib.loads(f.read())
def update(self):
"""
Updates some core things in the background.
"""
for timeout in self._timeouts.copy():
if timeout.fireOn <= time.perf_counter():
self.onEvent(timeout)
self._timeouts.remove(timeout)
def onEvent(self, event):
pass
def close(self):
pass
def createAnimObj(self, *args, **kwargs):
"""
Creates an animated object.
Args:
objectID (str): The ID of the object.
baseFrame (str): The path to the base frame.
"""
return AnimatedObject(self, *args, **kwargs)
def timeout(self, id: str, delay: float):
"""
Fires a Timeout event with the specified ID
after the specified delay.
Args:
id (str): The timeout ID.
delay (float): How long (in seconds)
to wait before firing.
"""
self._timeouts.append(Timeout(id, time.perf_counter()+delay))

36
src/gameUtils/events.py Normal file
View File

@ -0,0 +1,36 @@
import time
class _event:
def __init__(self):
self.type = self.__class__
class AnimStart(_event):
def __init__(self, objectID, animationID):
super().__init__()
self.objectID = objectID
self.animationID = animationID
def __repr__(self):
return f'<AnimFinish | {self.objectID=} | {self.animationID=}>'
class AnimFinish(_event):
def __init__(self, objectID, animationID):
super().__init__()
self.objectID = objectID
self.animationID = animationID
def __repr__(self):
return f'<AnimFinish | {self.objectID=} | {self.animationID=}>'
class Timeout(_event):
def __init__(self, timeoutID, fireOn):
super().__init__()
# The timeout ID specified by the user
self.timeoutID = timeoutID
# When the event should be fired
self.fireOn = fireOn
# When it was created
self.created = time.perf_counter()
def __repr__(self):
return f'<Timeout | {self.timeoutID=} | {self.fireOn=} | {self.created=}>'

22
src/gameUtils/utils.py Normal file
View File

@ -0,0 +1,22 @@
import pygame
def centre(surface: pygame.Surface, size: tuple[int, int]) -> pygame.Surface:
"""
Centres a surface within a given rectangle.
Args:
surface: The surface to be centred.
size: The size of the rectangle the surface will be centred in.
Returns:
pygame.Surface
"""
surf = pygame.Surface(size)
offX = surf.size[0]//2 - surface.size[0]//2
offY = surf.size[1]//2 - surface.size[1]//2
surf.blit(surface, (offX, offY))
return surf

View File

@ -1,14 +0,0 @@
import pygame
def centre(surface: pygame.Surface, rect: tuple[int, int, int, int]) -> pygame.Surface:
"""
Centres a surface within a given rectangle.
Args:
surface: The surface to be centred.
rect: The rectangle the surface will be centred in.
Returns:
pygame.Surface
"""
pass